diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 00000000..176ede93
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,38 @@
+# GitHub Copilot Instructions
+
+This document guides AI assistants when contributing to the Processor-Emulator repository.
+
+## 1. Project Overview
+- **Language & Framework**: C# (.NET 6) WPF application with a MainWindow UI and multiple emulator implementations.
+- **Core Purpose**: Load and analyze firmware images, emulate CPU architectures, probe filesystems, and extract data.
+- **Key Directories**:
+ - `/` root: main UI code (`App.xaml`, `MainWindow.xaml.cs`, `EmulatorLauncher.cs`)
+ - `/Emulation`: stub and SPARC emulators
+ - Files in root implement each chipset emulator and filesystem logic.
+
+## 2. Coding Conventions
+- **Naming**: PascalCase for public types and methods, _camelCase_ for private fields.
+- **Async Patterns**: Use `async Task` for I/O or longer-running operations and `await` inside.
+- **Helpers & Utilities**:
+ - Consolidate repeated logic in a single static helper.
+ - Place utility methods in `Tools.cs` or under a `Utilities` static class.
+- **UI Interaction**: Use `Dispatcher.Invoke` or `await` to update UI from background threads. Use shared `ShowTextWindow` helper for log output.
+
+## 3. AI Integration Guidelines
+- **Refactor Duplicates**: When encountering duplicate local functions (e.g., `AnalyzeFileType`, `ShowTextWindow`), merge into one shared definition without hitting request limit, length limit or creating invalid responses or getting stuck in loops.
+- **New Emulators**: Implement new `IChipsetEmulator` in its own file. Follow existing patterns: detect types, dispatch via `InstructionTranslator`.
+- **File Placement**: Match namespace and folder. Root-level emulators go in `/Emulation` folder or root if project-wide.
+- **Error Handling**: Leverage `try/catch` around disk I/O and emulator startup. Surface errors via `ShowTextWindow`.
+
+## 4. Build & Run Tasks
+- Use the existing `tasks.json`:
+ - **Build**: `dotnet build ProcessorEmulator.csproj`
+ - **Run**: `dotnet run --project ProcessorEmulator.csproj`
+- No unit tests present; ensure manual end-to-end validation of firmware loads.
+
+## 5. Pull Request Guidelines
+- Target `dev` branch. Keep PRs focused and small.
+- Include screenshots or logs for UI changes.
+- Verify compile and smoke-test emulation scenarios local to Windows/.NET 6.
+
+> _These instructions help AI assistants contribute consistently and correctly to the Processor-Emulator codebase._
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..e69de29b
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
new file mode 100644
index 00000000..e69de29b
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..16512eb4
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,30 @@
+name: CI
+
+on:
+ push:
+ branches: [dev, main]
+ pull_request:
+ branches: [dev, main]
+
+jobs:
+ build:
+ name: Build on Windows
+ runs-on: windows-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore
+
+ - name: Build solution
+ run: dotnet build ProcessorEmulator.csproj --configuration Release --no-restore
+
+ - name: Run tests (if any)
+ run: |
+ echo "Skipping tests: no test projects defined."
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 00000000..7e2ab1c8
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,24 @@
+name: "CodeQL Analysis"
+
+on:
+ push:
+ branches: [dev, main]
+ pull_request:
+ branches: [dev, main]
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: csharp
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml
new file mode 100644
index 00000000..105c536a
--- /dev/null
+++ b/.github/workflows/dependabot.yml
@@ -0,0 +1,7 @@
+version: 2
+updates:
+ - package-ecosystem: "nuget"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 5
diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml
new file mode 100644
index 00000000..e636e6b7
--- /dev/null
+++ b/.github/workflows/dotnet-build.yml
@@ -0,0 +1,26 @@
+name: ".NET Build"
+
+on:
+ push:
+ branches: [dev, main]
+ pull_request:
+ branches: [dev, main]
+
+jobs:
+ build:
+ name: build
+ runs-on: windows-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore
+
+ - name: Build solution
+ run: dotnet build Processor-Emulator.sln --configuration Release --no-restore
diff --git a/.github/workflows/dotnet-desktop-build.yml b/.github/workflows/dotnet-desktop-build.yml
new file mode 100644
index 00000000..7666a178
--- /dev/null
+++ b/.github/workflows/dotnet-desktop-build.yml
@@ -0,0 +1,28 @@
+name: ".NET Core Desktop build"
+
+on:
+ push:
+ branches: [dev, main]
+ pull_request:
+ branches: [dev, main]
+
+jobs:
+ build-debug:
+ name: build (Debug)
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+ - run: dotnet build ProcessorEmulator.csproj --configuration Debug
+
+ build-release:
+ name: build (Release)
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+ - run: dotnet build ProcessorEmulator.csproj --configuration Release
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 79e2ded8..5ce2fe5b 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -2,9 +2,9 @@ name: .NET Build
on:
push:
- branches: [ main, master ]
+ branches: [ dev, main ]
pull_request:
- branches: [ main, master ]
+ branches: [ dev, main ]
jobs:
build:
diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml
new file mode 100644
index 00000000..0538673a
--- /dev/null
+++ b/.github/workflows/nightly-build.yml
@@ -0,0 +1,34 @@
+name: Nightly Build
+
+on:
+ schedule:
+ # every day at 3am UTC
+ - cron: '0 3 * * *'
+
+jobs:
+ build:
+ name: Daily Windows Build
+ runs-on: windows-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore
+
+ - name: Build solution
+ run: dotnet build ProcessorEmulator.csproj --configuration Release --no-restore
+
+ - name: Publish artifacts
+ run: dotnet publish ProcessorEmulator.csproj --configuration Release --output artifacts
+
+ - name: Upload nightly artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: nightly-build
+ path: artifacts
diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
new file mode 100644
index 00000000..17acb0fe
--- /dev/null
+++ b/.github/workflows/release-drafter.yml
@@ -0,0 +1,35 @@
+name: Release Drafter
+
+on:
+ push:
+ branches: [dev, main]
+
+jobs:
+ update_release_draft:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: release-drafter/release-drafter@v5
+ with:
+ config-name: "release-drafter.yml"
+ template: |
+ # 🎉 Release {{ version }}
+
+ Hello everyone!
+
+ We’re excited to share **{{ version }}** with you. Here’s what’s new:
+
+ {{#changes}}
+ {{> change}}
+ {{/changes}}
+
+ A heartfelt thank you to all contributors:
+ {{#contributors}}
+ - @{{this}}
+ {{/contributors}}
+
+ Stay tuned for more updates!
+ change-template: |
+ {{#if is:added}}✨ **New** {{description}} (thanks @{{author}})!{{/if}}
+ {{#if is:changed}}🔄 **Updated** {{description}} (thanks @{{author}})!{{/if}}
+ {{#if is:fixed}}🐞 **Fixed** {{description}} (thanks @{{author}})!{{/if}}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..882b477d
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,48 @@
+name: "Release on Tag"
+
+on:
+ push:
+ # Trigger on git tags, e.g. v1.2.3
+ tags:
+ - 'v*'
+
+jobs:
+ build-and-release:
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup .NET 6 SDK
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Build solution
+ run: |
+ dotnet restore
+ dotnet build ProcessorEmulator.csproj --configuration Release
+
+ - name: Publish artifacts
+ run: |
+ dotnet publish ProcessorEmulator.csproj --configuration Release --output artifacts
+
+ - name: Create GitHub Release
+ id: create_release
+ uses: actions/create-release@v1
+ with:
+ tag_name: ${{ github.ref_name }}
+ release_name: Release ${{ github.ref_name }}
+ draft: false
+ prerelease: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload Release Asset
+ uses: actions/upload-release-asset@v1
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: artifacts/ProcessorEmulator.exe
+ asset_name: ProcessorEmulator-${{ github.ref_name }}.exe
+ asset_content_type: application/octet-stream
diff --git a/.gitignore b/.gitignore
index 6e78d5af..0bf844a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -256,4 +256,4 @@ bin/Release/net6.0-windows/share/locale/tr/LC_MESSAGES/qemu.mo
bin/Release/net6.0-windows/share/locale/uk/LC_MESSAGES/qemu.mo
bin/Release/net6.0-windows/share/locale/zh_CN/LC_MESSAGES/qemu.mo
bin/Release/net6.0-windows/share/man/man1/qemu.1
-/.github
+
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..09b458bb
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,44 @@
+{
+ // Disable minimap for faster rendering
+ "editor.minimap.enabled": false,
+ // Reduce visual decorations
+ "editor.renderWhitespace": "none",
+ "editor.folding": false,
+ "editor.codeLens": false,
+ "editor.semanticHighlighting.enabled": false,
+
+ // Exclude build folders from file watchers and searches
+ "files.watcherExclude": {
+ "**/bin/**": true,
+ "**/obj/**": true
+ },
+ "search.exclude": {
+ "**/bin/**": true,
+ "**/obj/**": true
+ },
+
+ // Optimize C# performance
+ "omnisharp.enableRoslynAnalyzers": false,
+ "omnisharp.useModernNet": false,
+ "omnisharp.msbuildLogLevel": "quiet",
+ // Disable inlay hints to avoid LSP requests on null documents
+ "editor.inlayHints.enabled": "off",
+ "[csharp]": {
+ "editor.inlayHints.enabled": "off"
+ },
+ // Disable C# specific inlay hint settings
+ "csharp.inlayHints.parameterNames.enabled": "none",
+ "csharp.inlayHints.parameterTypes.enabled": "none",
+ "csharp.inlayHints.variableTypes.enabled": "none",
+
+ // Disable outline and breadcrumbs to reduce symbol requests
+ "outline.showFiles": false,
+ "breadcrumbs.enabled": false,
+
+ // Disable telemetry for reduced background activity
+ "telemetry.enableTelemetry": false,
+ "telemetry.telemetryLevel": "off",
+
+ // YAML: disable built-in validation to ignore ci.yml errors
+ "yaml.validate": false
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..c464090b
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,19 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build Project",
+ "type": "shell",
+ "command": "dotnet build \"${workspaceFolder}/ProcessorEmulator.csproj\" --configuration Debug",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "shared"
+ },
+ "problemMatcher": ["$msCompile"]
+ }
+ ]
+}
diff --git a/ArchiveExtractor.cs b/ArchiveExtractor.cs
index a9091b3d..8bdf0f14 100644
--- a/ArchiveExtractor.cs
+++ b/ArchiveExtractor.cs
@@ -1,3 +1,4 @@
+using System;
using System.Diagnostics;
using System.IO;
@@ -7,13 +8,26 @@ public static class ArchiveExtractor
{
public static void ExtractArchive(string archivePath, string outputDir)
{
- // Assumes 7z.exe is in PATH or same directory as the app
+ // Resolve 7z executable path
+ string sevenZip = Resolve7zExecutable();
+ if (string.IsNullOrEmpty(sevenZip))
+ {
+ // Fallback for ZIP archives using built-in .NET extraction
+ if (Path.GetExtension(archivePath).Equals(".zip", StringComparison.OrdinalIgnoreCase))
+ {
+ System.IO.Compression.ZipFile.ExtractToDirectory(archivePath, outputDir);
+ SanitizeExtraction(outputDir);
+ return;
+ }
+ throw new InvalidOperationException("7z.exe not found. Please install 7-Zip or locate 7z.exe manually.");
+ }
+ // Use -spf to allow extraction of full paths
var process = new Process
{
StartInfo = new ProcessStartInfo
{
- FileName = "7z.exe",
- Arguments = $"x \"{archivePath}\" -o\"{outputDir}\" -y",
+ FileName = sevenZip,
+ Arguments = $"x -spf \"{archivePath}\" -o\"{outputDir}\" -y",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
@@ -22,12 +36,78 @@ public static void ExtractArchive(string archivePath, string outputDir)
};
process.Start();
process.WaitForExit();
+ // Remove any files extracted outside of the output directory (prevent overwriting host paths)
+ SanitizeExtraction(outputDir);
}
public static void ExtractAndAnalyze(string archivePath, string outputDir)
{
ExtractArchive(archivePath, outputDir);
+ // Further sanitize before analysis
FirmwareAnalyzer.AnalyzeFirmwareArchive(archivePath, outputDir);
}
+
+ ///
+ /// Deletes any files or directories unintentionally placed outside the extraction root.
+ ///
+ private static void SanitizeExtraction(string outputDir)
+ {
+ try
+ {
+ var root = Path.GetFullPath(outputDir).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
+ // Check all files under the extraction parent path
+ var parent = Path.GetDirectoryName(root);
+ foreach (var file in Directory.GetFiles(parent, "*", SearchOption.AllDirectories))
+ {
+ var full = Path.GetFullPath(file);
+ if (!full.StartsWith(root, StringComparison.OrdinalIgnoreCase))
+ {
+ File.Delete(full);
+ }
+ }
+ // Optionally, similar cleanup for directories can be added
+ }
+ catch { /* Ignore cleanup errors */ }
+ }
+
+ ///
+ /// Locates 7z.exe via PATH, common install folders, or user prompt.
+ ///
+ private static string Resolve7zExecutable()
+ {
+ // Check common Program Files locations
+ string[] candidates = new[] {
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-Zip", "7z.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "7-Zip", "7z.exe")
+ };
+ foreach (var path in candidates)
+ {
+ if (File.Exists(path)) return path;
+ }
+ // Search in PATH
+ var paths = Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? Array.Empty();
+ foreach (var dir in paths)
+ {
+ try
+ {
+ var exe = Path.Combine(dir.Trim(), "7z.exe");
+ if (File.Exists(exe)) return exe;
+ }
+ catch { }
+ }
+ // Prompt user to locate 7z.exe
+ try
+ {
+ var dlg = new Microsoft.Win32.OpenFileDialog
+ {
+ Title = "Locate 7z.exe",
+ Filter = "7-Zip Executable (7z.exe)|7z.exe"
+ };
+ if (dlg.ShowDialog() == true)
+ return dlg.FileName;
+ }
+ catch { }
+ return null;
+ }
}
}
diff --git a/ArmCpuEmulator.cs b/ArmCpuEmulator.cs
index 1dbc000c..ccfab81a 100644
--- a/ArmCpuEmulator.cs
+++ b/ArmCpuEmulator.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
namespace ProcessorEmulator.Emulation
{
diff --git a/BinaryScanner.cs b/BinaryScanner.cs
index 6fb16300..2f76bac5 100644
--- a/BinaryScanner.cs
+++ b/BinaryScanner.cs
@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Linq;
-using System.Collections.Generic;
namespace ProcessorEmulator.Tools
{
diff --git a/DirecTVEmulator.cs b/DirecTVEmulator.cs
index f58557ca..1b02c6ee 100644
--- a/DirecTVEmulator.cs
+++ b/DirecTVEmulator.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
namespace ProcessorEmulator.Emulation
{
diff --git a/DvrVxWorksDetector.cs b/DvrVxWorksDetector.cs
index 97f9bd51..d2064e47 100644
--- a/DvrVxWorksDetector.cs
+++ b/DvrVxWorksDetector.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
using System.Collections.Generic;
using System.Text;
diff --git a/Emulation.cs b/Emulation.cs
index d2165781..f771fa6a 100644
--- a/Emulation.cs
+++ b/Emulation.cs
@@ -1,6 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
namespace ProcessorEmulator.Emulation
{
diff --git a/Emulation/HomebrewEmulator.cs b/Emulation/HomebrewEmulator.cs
index 5e0925c2..36efdf69 100644
--- a/Emulation/HomebrewEmulator.cs
+++ b/Emulation/HomebrewEmulator.cs
@@ -1,4 +1,7 @@
using System;
+using System.Windows;
+using System.Diagnostics;
+using ProcessorEmulator.Tools;
namespace ProcessorEmulator.Emulation
{
@@ -8,20 +11,22 @@ namespace ProcessorEmulator.Emulation
///
public class HomebrewEmulator : IEmulator
{
- private readonly string architecture;
+ // Architecture is auto-detected from original binary
+ private byte[] originalBinary;
private byte[] memory;
private uint[] regs = new uint[32];
private uint pc;
private Tools.DeviceTreeManager.Node deviceTree;
- public HomebrewEmulator(string architecture)
+ public HomebrewEmulator()
{
- this.architecture = architecture;
+ // No-op: architecture will be detected at runtime
}
public void LoadBinary(byte[] binary)
{
- // Load firmware into memory (256MB)
+ // Store original binary and load firmware into memory (256MB)
+ originalBinary = binary;
memory = new byte[256 * 1024 * 1024];
Array.Copy(binary, 0, memory, 0, binary.Length);
// Detect Flattened Device Tree (FDT) blob in memory (magic 0xd00dfeed)
@@ -43,8 +48,11 @@ public void LoadBinary(byte[] binary)
public void Run()
{
- // Dispatch by architecture
- if (architecture.StartsWith("MIPS", StringComparison.OrdinalIgnoreCase))
+ // Auto-detect architecture from original binary
+ var detectedArch = ArchitectureDetector.Detect(originalBinary);
+ Debug.WriteLine($"HomebrewEmulator: auto-detected architecture: {detectedArch}");
+ // Dispatch by detected architecture
+ if (detectedArch.StartsWith("MIPS", StringComparison.OrdinalIgnoreCase))
{
// MIPS32 fetch-decode-execute
const int maxSteps = 1000000;
@@ -52,7 +60,7 @@ public void Run()
Step();
return;
}
- else if (architecture.StartsWith("ARM", StringComparison.OrdinalIgnoreCase))
+ else if (detectedArch.StartsWith("ARM", StringComparison.OrdinalIgnoreCase))
{
// Delegate to ARM emulator stub
var armEmu = new ArmEmulator();
@@ -60,8 +68,119 @@ public void Run()
armEmu.Run();
return;
}
- // Add other ISA dispatch as needed
- throw new NotImplementedException($"Homebrew emulator not implemented for architecture: {architecture}");
+ // Additional ISA dispatch
+ else if (detectedArch.StartsWith("X86", StringComparison.OrdinalIgnoreCase) ||
+ detectedArch.StartsWith("I386", StringComparison.OrdinalIgnoreCase))
+ {
+ var x86Emu = new X86CpuEmulator();
+ x86Emu.LoadProgram(memory, 0);
+ x86Emu.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("SPARC64", StringComparison.OrdinalIgnoreCase))
+ {
+ var sparc64 = new Sparc64Emulator();
+ sparc64.LoadBinary(memory);
+ sparc64.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("SPARC", StringComparison.OrdinalIgnoreCase))
+ {
+ var sparc = new SparcEmulator();
+ sparc.LoadBinary(memory);
+ sparc.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("ALPHA", StringComparison.OrdinalIgnoreCase))
+ {
+ var alpha = new AlphaEmulator();
+ alpha.LoadBinary(memory);
+ alpha.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("SUPERH", StringComparison.OrdinalIgnoreCase) ||
+ detectedArch.StartsWith("SH", StringComparison.OrdinalIgnoreCase))
+ {
+ var sh = new SuperHEmulator();
+ sh.LoadBinary(memory);
+ sh.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("RISCV32", StringComparison.OrdinalIgnoreCase))
+ {
+ var rv32 = new RiscV32Emulator();
+ rv32.LoadBinary(memory);
+ rv32.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("RISCV64", StringComparison.OrdinalIgnoreCase))
+ {
+ var rv64 = new RiscV64Emulator();
+ rv64.LoadBinary(memory);
+ rv64.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("S390X", StringComparison.OrdinalIgnoreCase))
+ {
+ var s390 = new S390XEmulator();
+ s390.LoadBinary(memory);
+ s390.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("HPPA", StringComparison.OrdinalIgnoreCase))
+ {
+ var hppa = new HppaEmulator();
+ hppa.LoadBinary(memory);
+ hppa.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("MICROBLAZE", StringComparison.OrdinalIgnoreCase))
+ {
+ var mb = new MicroBlazeEmulator();
+ mb.LoadBinary(memory);
+ mb.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("CRIS", StringComparison.OrdinalIgnoreCase))
+ {
+ var cris = new CrisEmulator();
+ cris.LoadBinary(memory);
+ cris.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("LM32", StringComparison.OrdinalIgnoreCase))
+ {
+ var lm = new Lm32Emulator();
+ lm.LoadBinary(memory);
+ lm.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("M68K", StringComparison.OrdinalIgnoreCase))
+ {
+ var m68k = new M68KEmulator();
+ m68k.LoadBinary(memory);
+ m68k.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("XTENSA", StringComparison.OrdinalIgnoreCase))
+ {
+ var xt = new XtensaEmulator();
+ xt.LoadBinary(memory);
+ xt.Run();
+ return;
+ }
+ else if (detectedArch.StartsWith("OPENRISC", StringComparison.OrdinalIgnoreCase))
+ {
+ var or = new OpenRiscEmulator();
+ or.LoadBinary(memory);
+ or.Run();
+ return;
+ }
+ // Unknown or unsupported architecture: warn and fallback to QEMU
+ MessageBox.Show(
+ $"Homebrew emulator not implemented for architecture: {detectedArch}.\nFalling back to QEMU emulation.",
+ "Homebrew Emulator", MessageBoxButton.OK, MessageBoxImage.Warning);
+ throw new NotImplementedException($"Homebrew emulator not implemented for architecture: {detectedArch}");
}
public void Step()
diff --git a/EmulatorLauncher.cs b/EmulatorLauncher.cs
index a7213fb2..11a350c3 100644
--- a/EmulatorLauncher.cs
+++ b/EmulatorLauncher.cs
@@ -1,4 +1,3 @@
-using System;
using ProcessorEmulator.Tools;
namespace ProcessorEmulator.Emulation
diff --git a/ExoticFilesystemManager.cs b/ExoticFilesystemManager.cs
index c84ac79e..996da829 100644
--- a/ExoticFilesystemManager.cs
+++ b/ExoticFilesystemManager.cs
@@ -3,9 +3,7 @@
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using ProcessorEmulator.Tools.FileSystems;
-using System.Reflection;
namespace ProcessorEmulator.Tools
{
diff --git a/FileSystems.cs b/FileSystems.cs
index 391d239a..98e5567b 100644
--- a/FileSystems.cs
+++ b/FileSystems.cs
@@ -2,7 +2,6 @@
using System.IO;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.InteropServices;
namespace ProcessorEmulator.Tools.FileSystems
{
diff --git a/FilesystemProber.cs b/FilesystemProber.cs
index 88bf5754..e0218838 100644
--- a/FilesystemProber.cs
+++ b/FilesystemProber.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Collections.Generic;
namespace ProcessorEmulator.Tools
{
diff --git a/FirmwareAnalyzer.cs b/FirmwareAnalyzer.cs
index 4b7a09d8..c7720559 100644
--- a/FirmwareAnalyzer.cs
+++ b/FirmwareAnalyzer.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
namespace ProcessorEmulator.Tools
{
diff --git a/LinuxFileSystems.cs b/LinuxFileSystems.cs
index 0c9028e2..fc8c66fb 100644
--- a/LinuxFileSystems.cs
+++ b/LinuxFileSystems.cs
@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Collections.Generic;
-using System.Security.Cryptography;
namespace ProcessorEmulator.Tools.FileSystems
{
diff --git a/MainWindow.xaml b/MainWindow.xaml
index 4349d660..44287629 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -6,9 +6,18 @@
@@ -19,6 +28,9 @@
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index d9042355..45405f3e 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -5,9 +5,12 @@
using Microsoft.Win32;
using System.Threading.Tasks;
using System.IO;
+using System.Linq;
using System;
using System.Windows.Controls;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
namespace ProcessorEmulator
{
@@ -31,14 +34,19 @@ public interface IQemuEmulator : IEmulator
public partial class MainWindow : Window, IMainWindow
{
+
private IEmulator currentEmulator;
// Add a default constructor for XAML
public MainWindow()
{
- // Optionally initialize fields here
+ // Load XAML UI components
+ // Initialize drag-and-drop for file support
+ this.AllowDrop = true;
+ this.Drop += MainWindow_Drop;
}
+
public MainWindow(IEmulator currentEmulator)
{
this.currentEmulator = currentEmulator;
@@ -98,7 +106,9 @@ private async void StartEmulation_Click(object sender,
"Emulate CMTS Head End",
"Uverse Box Emulator",
"DirecTV Box/Firmware Analysis",
- "Linux Filesystem Read/Write"
+ "Executable Analysis",
+ "Linux Filesystem Read/Write",
+ "Cross-Compile Binary"
};
string mainChoice = PromptUserForChoice("What would you like to do?", mainOptions);
if (string.IsNullOrEmpty(mainChoice)) return;
@@ -132,9 +142,15 @@ private async void StartEmulation_Click(object sender,
case "DirecTV Box/Firmware Analysis":
await HandleDirectvAnalysis();
break;
+ case "Executable Analysis":
+ await HandleExecutableAnalysis();
+ break;
case "Linux Filesystem Read/Write":
await HandleLinuxFsReadWrite();
break;
+ case "Cross-Compile Binary":
+ await HandleCrossCompile();
+ break;
default:
MessageBox.Show("Not implemented yet.", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
break;
@@ -169,18 +185,28 @@ private async Task HandleGenericEmulation()
try
{
// First attempt homebrew emulation
- var home = new HomebrewEmulator(arch);
+ var home = new HomebrewEmulator();
home.LoadBinary(binary);
home.Run();
StatusBarText("Homebrew emulation complete.");
}
catch (NotImplementedException)
{
- // Fallback to QEMU
+ // Fallback to QEMU with optional extra CLI options
StatusBarText("Homebrew emulator not implemented for this architecture, falling back to QEMU...");
+ // Prompt user for extra QEMU command-line options using a detailed dialog
+ string extraArgs = PromptForQemuOptions();
try
{
- EmulatorLauncher.Launch(filePath, arch);
+ if (!string.IsNullOrEmpty(extraArgs))
+ {
+ // Use QemuManager directly for extra arguments
+ ProcessorEmulator.Tools.QemuManager.LaunchWithArgs(filePath, arch, extraArgs);
+ }
+ else
+ {
+ EmulatorLauncher.Launch(filePath, arch);
+ }
StatusBarText("QEMU emulation started.");
}
catch (Exception qex)
@@ -232,34 +258,75 @@ private async Task HandleUverseEmulation()
{
var openFileDialog = new OpenFileDialog
{
- Filter = "Uverse Boot or Signature File (*.bin;*.sig)|*.bin;*.sig|All Files (*.*)|*.*"
+ Filter = "Firmware Images (*.bin;*.img;*.sig)|*.bin;*.img;*.sig|All Files (*.*)|*.*"
};
if (openFileDialog.ShowDialog() != true) return;
- string sigPath = openFileDialog.FileName;
- StatusBarText($"Loading Uverse configuration from {Path.GetFileName(sigPath)}...");
-
- // Example hardware config: adjust as needed or prompt user
- var config = new UverseHardwareConfig
+ string filePath = openFileDialog.FileName;
+ StatusBarText($"Selected file: {Path.GetFileName(filePath)}");
+ try
{
- ModelType = "VIP2250",
- ProcessorType = "ARM",
- MemorySize = 128 * 1024 * 1024,
- IsDVR = true,
- IsWholeHome = false
- };
- var emulator = new UverseEmulator(config);
- emulator.LoadBootImage(sigPath);
-
- // Attempt to load content signature in same folder
- string contentSig = Path.Combine(Path.GetDirectoryName(sigPath), "content.sig");
- if (File.Exists(contentSig))
- emulator.LoadMediaroomContent(contentSig);
-
- emulator.EmulateWholeHomeNetwork();
- UverseEmulator.StartMediaroom();
+ string ext = Path.GetExtension(filePath).ToLowerInvariant();
+ string extractDir = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath) + "_extracted");
+ if (ext != ".sig")
+ {
+ // Perform generic firmware analysis
+ await Task.Run(() => ArchiveExtractor.ExtractAndAnalyze(filePath, extractDir));
+ FirmwareAnalyzer.AnalyzeFirmwareArchive(filePath, extractDir);
+ StatusBarText("Firmware analysis complete.");
+ MessageBox.Show($"Firmware {Path.GetFileName(filePath)} analyzed in {extractDir}", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
- StatusBarText("Uverse emulation complete.");
+ // Signature-based Uverse emulation
+ StatusBarText($"Loading Uverse config from {Path.GetFileName(filePath)}...");
+ string model = PromptUserForInput("Enter model type (e.g. VIP2250):")?.Trim();
+ if (string.IsNullOrWhiteSpace(model)) model = "VIP2250";
+ string proc = PromptUserForInput("Enter processor type (e.g. ARM/x86):")?.Trim();
+ if (string.IsNullOrWhiteSpace(proc)) proc = "ARM";
+ string memInput = PromptUserForInput("Enter memory size in MB:")?.Trim();
+ if (!int.TryParse(memInput, out int mb)) mb = 128;
+ uint memBytes = (uint)(mb * 1024 * 1024);
+ bool isDVR = PromptUserForChoice("Is this device a DVR?", new[] { "Yes", "No" }) == "Yes";
+ bool isWholeHome = PromptUserForChoice("Enable Whole Home network?", new[] { "Yes", "No" }) == "Yes";
+ var config = new UverseHardwareConfig
+ {
+ ModelType = model,
+ ProcessorType = proc,
+ MemorySize = memBytes,
+ IsDVR = isDVR,
+ IsWholeHome = isWholeHome
+ };
+ var emulator = new UverseEmulator(config);
+ ShowTextWindow("Uverse Emulation", new List { "Initialized emulator with config." });
+ emulator.LoadBootImage(filePath);
+ ShowTextWindow("Uverse Emulation", new List { $"Loaded boot image: {Path.GetFileName(filePath)}" });
+ string contentSig = Path.Combine(Path.GetDirectoryName(filePath), "content.sig");
+ if (File.Exists(contentSig))
+ {
+ emulator.LoadMediaroomContent(contentSig);
+ ShowTextWindow("Uverse Emulation", new List { $"Loaded content signatures: {Path.GetFileName(contentSig)}" });
+ }
+ emulator.EmulateWholeHomeNetwork();
+ ShowTextWindow("Uverse Emulation", new List { "Configured whole home network." });
+ UverseEmulator.StartMediaroom();
+ ShowTextWindow("Uverse Emulation", new List { "Started Mediaroom platform." });
+ var uverseLog = new List
+ {
+ $"Model: {model}",
+ $"Processor: {proc}",
+ $"Memory (MB): {mb}",
+ $"DVR Enabled: {isDVR}",
+ $"Whole Home Network: {isWholeHome}"
+ };
+ ShowTextWindow("Uverse Emulation Log", uverseLog);
+ StatusBarText("Uverse emulation complete.");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Uverse processing failed: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ StatusBarText("Uverse processing failed.");
+ }
await Task.CompletedTask;
}
@@ -278,15 +345,129 @@ private async Task HandleDirectvAnalysis()
if (openFileDialog.ShowDialog() == true)
{
string filePath = openFileDialog.FileName;
- StatusBarText($"Selected DirecTV firmware: {Path.GetFileName(filePath)}");
- string extractDir = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath) + "_directv_extracted");
- await Task.Run(() => ArchiveExtractor.ExtractAndAnalyze(filePath, extractDir));
- StatusBarText("DirecTV firmware extraction and analysis complete.");
- MessageBox.Show($"DirecTV firmware {Path.GetFileName(filePath)} extracted and analyzed to {extractDir}.", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+ StatusBarText($"Selected firmware: {Path.GetFileName(filePath)}");
+ string extractDir = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath) + "_extracted");
+ try
+ {
+ // Extract archive and analyze file structure
+ await Task.Run(() => ArchiveExtractor.ExtractAndAnalyze(filePath, extractDir));
+ // Further analyze binaries in the extracted directory
+ FirmwareAnalyzer.AnalyzeFirmwareArchive(filePath, extractDir);
+ StatusBarText("Firmware extraction and analysis complete.");
+ MessageBox.Show($"Firmware {Path.GetFileName(filePath)} extracted and analyzed to {extractDir}.", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Analysis failed: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ StatusBarText("Firmware analysis failed.");
+ }
}
await Task.CompletedTask;
}
+ ///
+ /// Analyzes arbitrary executables or binaries to detect architecture and format.
+ ///
+ private async Task HandleExecutableAnalysis()
+ {
+ // Select executable file
+ var dlg = new OpenFileDialog
+ {
+ Filter = "Executables and Binaries (*.exe;*.dll;*.bin;*.so)|*.exe;*.dll;*.bin;*.so|All Files (*.*)|*.*"
+ };
+ if (dlg.ShowDialog() != true) return;
+ string filePath = dlg.FileName;
+ StatusBarText($"Analyzing executable: {Path.GetFileName(filePath)}");
+ byte[] data = File.ReadAllBytes(filePath);
+ // Determine format and architecture
+ string format = (data.Length > 4 && data[0] == 0x7F && data[1] == (byte)'E' && data[2] == (byte)'L' && data[3] == (byte)'F') ? "ELF" : "PE";
+ string arch = ArchitectureDetector.Detect(data);
+ string bitness = "Unknown";
+ if (format == "PE" && data.Length > 0x40)
+ {
+ int peOffset = BitConverter.ToInt32(data, 0x3C);
+ ushort machine = BitConverter.ToUInt16(data, peOffset + 4);
+ bitness = machine switch
+ {
+ 0x14c => "x86",
+ 0x8664 => "x64",
+ 0x1c0 => "ARM",
+ 0xaa64 => "ARM64",
+ _ => "Unknown"
+ };
+ }
+ else if (format == "ELF" && data.Length > 5)
+ {
+ bitness = data[4] == 1 ? "32-bit" : data[4] == 2 ? "64-bit" : "Unknown";
+ }
+ var output = new List
+ {
+ $"File: {Path.GetFileName(filePath)}",
+ $"Format: {format}",
+ $"Architecture: {arch}",
+ $"Bitness: {bitness}"
+ };
+ // Encourage contribution for unsupported chips
+ var desc = ChipReferenceManager.GetInfo(arch);
+ if (!string.IsNullOrEmpty(desc))
+ output.Add($"Description: {desc}");
+ else
+ output.Add(ChipReferenceManager.GetContributionMessage(arch));
+ ShowTextWindow("Executable Analysis", output);
+ StatusBarText("Executable analysis complete.");
+ // Prompt to launch emulator
+ var choice = PromptUserForChoice("Launch emulator for this executable?", new[] { "Homebrew", "QEMU", "No" });
+ if (choice == "Homebrew")
+ {
+ try
+ {
+ var home = new HomebrewEmulator();
+ home.LoadBinary(data);
+ home.Run();
+ StatusBarText("Homebrew emulation complete.");
+ }
+ catch (NotImplementedException)
+ {
+ MessageBox.Show("Homebrew emulator not supported for this architecture.", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ }
+ else if (choice == "QEMU")
+ {
+ try
+ {
+ EmulatorLauncher.Launch(filePath, arch);
+ StatusBarText("QEMU emulation started.");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Emulation error: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+ await Task.CompletedTask;
+ }
+
+ #if false
+ private async Task HandleFirmadyneEmulation()
+ {
+ StatusBarText("Starting Firmadyne-based emulation...");
+ // TODO: Integrate Firmadyne pipeline (extract rootfs, QEMU setup, network capture)
+ ShowTextWindow("Firmadyne Emulation", new List { "Firmadyne integration not implemented yet." });
+ StatusBarText("Firmadyne emulation stub complete.");
+ await Task.CompletedTask;
+ }
+ #endif
+
+ #if false
+ private async Task HandleAzeriaEmulation()
+ {
+ StatusBarText("Starting Azeria Labs ARM firmware emulation...");
+ // TODO: Follow steps from https://azeria-labs.com/emulating-arm-firmware/ to setup QEMU and load firmware
+ ShowTextWindow("Azeria ARM Emulation", new List { "Azeria ARM firmware emulation not implemented yet." });
+ StatusBarText("Azeria ARM emulation stub complete.");
+ await Task.CompletedTask;
+ }
+ #endif
+
// Core feature handlers
///
@@ -299,7 +480,10 @@ private async Task HandleRdkVEmulation()
string path = dlg.FileName;
StatusBarText($"Launching RDK-V emulator for {Path.GetFileName(path)}...");
var bin = File.ReadAllBytes(path);
- var arch = ArchitectureDetector.Detect(bin);
+ // Force ARM architecture for RDK-V hardware
+ var arch = "ARM";
+ Debug.WriteLine("Forcing architecture to ARM for RDK-V hardware.");
+ StatusBarText("Forcing ARM architecture for RDK-V hardware.");
try { EmulatorLauncher.Launch(path, arch, platform: "RDK-V"); StatusBarText("RDK-V emulation started."); }
catch (Exception ex) { MessageBox.Show($"RDK-V error: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); StatusBarText("RDK-V emulation failed."); }
await Task.CompletedTask;
@@ -338,6 +522,46 @@ private async Task HandleLinuxFsReadWrite()
await Task.CompletedTask;
}
+ ///
+ /// Cross-compiles a binary from one architecture to another.
+ ///
+ private async Task HandleCrossCompile()
+ {
+ var dlg = new OpenFileDialog { Filter = "Binaries (*.bin;*.exe;*.dll)|*.bin;*.exe;*.dll|All Files (*.*)|*.*" };
+ if (dlg.ShowDialog() != true) return;
+ string inputPath = dlg.FileName;
+ StatusBarText($"Cross-compiling {Path.GetFileName(inputPath)}...");
+ byte[] inputData = File.ReadAllBytes(inputPath);
+ string fromArch = ArchitectureDetector.Detect(inputData);
+ var targets = new[] { "x86", "x64", "ARM", "ARM64" };
+ string toArch = PromptUserForChoice("Select target architecture:", targets);
+ if (string.IsNullOrEmpty(toArch)) return;
+ // If this is a WinCE binary, launch emulator instead of static cross-compilation
+ if (IsWinCEBinary(inputData))
+ {
+ MessageBox.Show("WinCE binary detected; launching built-in emulator.", "WinCE Detected", MessageBoxButton.OK, MessageBoxImage.Information);
+ try
+ {
+ EmulatorLauncher.Launch(inputPath, fromArch);
+ StatusBarText("WinCE emulation started.");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"WinCE emulation error: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ StatusBarText("WinCE emulation failed.");
+ }
+ return;
+ }
+ // perform translation/recompile
+ byte[] outputData = ReadAndTranslateFile(inputPath, fromArch, toArch);
+ var saveDlg = new SaveFileDialog { Filter = "Binary Output (*.bin)|*.bin|All Files (*.*)|*.*", FileName = Path.GetFileNameWithoutExtension(inputPath) + $"_{toArch}" };
+ if (saveDlg.ShowDialog() != true) return;
+ File.WriteAllBytes(saveDlg.FileName, outputData);
+ ShowTextWindow("Cross-Compile Result", new List { $"Compiled from {fromArch} to {toArch} -> {Path.GetFileName(saveDlg.FileName)}" });
+ StatusBarText("Cross-compilation complete.");
+ await Task.CompletedTask;
+ }
+
private void ShowTextWindow(string title, List lines)
{
var win = new Window
@@ -360,6 +584,19 @@ private void ShowTextWindow(string title, List lines)
win.Show();
}
+ // Menu event handlers to toggle Unicorn engine usage
+ private void UseUnicorn_Checked(object sender, RoutedEventArgs e)
+ {
+ BinaryTranslator.UseUnicornEngine = true;
+ StatusBarText("Unicorn engine enabled");
+ }
+
+ private void UseUnicorn_Unchecked(object sender, RoutedEventArgs e)
+ {
+ BinaryTranslator.UseUnicornEngine = false;
+ StatusBarText("Unicorn engine disabled");
+ }
+
private static bool IsWinCEBinary(byte[] binary)
{
// Check PE header and subsystem for WinCE
@@ -438,6 +675,10 @@ private void LoadFirmwareImage(string imagePath, string signaturePath)
chipsetName = "Contoso6311";
else if (fwStr.Contains("FooChip9000"))
chipsetName = "FooChip9000";
+ else if (fwStr.Contains("BCM7405"))
+ chipsetName = "BCM7405";
+ else if (fwStr.Contains("MIPS 4380") || fwStr.Contains("MIPS4380"))
+ chipsetName = "MIPS4380";
// Add more heuristics as needed
// Example heuristic: look for filesystem markers
@@ -514,14 +755,21 @@ private string PromptUserForInput(string message)
return result;
}
+
private byte[] ReadAndTranslateFile(string filePath, string fromArch, string toArch)
{
- // Read a file and translate/virtualize (example)
- byte[] fileData = fsManager.ReadFile(filePath);
-
- // Translate and Virtualize
- byte[] result = fsManager.RunTranslatedAndVirtualized(fileData, fromArch, toArch);
- return result;
+ // Load raw data
+ byte[] data = File.ReadAllBytes(filePath);
+ try
+ {
+ // Attempt static cross-translation via BinaryTranslator
+ return BinaryTranslator.Translate(fromArch, toArch, data);
+ }
+ catch (NotImplementedException)
+ {
+ MessageBox.Show($"Translation from {fromArch} to {toArch} not implemented.", "Cross-Compile Error", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return data;
+ }
}
// Removed override of Equals(object) because DependencyObject.Equals(object) is sealed and cannot be overridden.
@@ -534,6 +782,69 @@ private void OpenMenuItem_Click(object sender, RoutedEventArgs e)
StartEmulation_Click(sender, e);
}
+ // New handler for firmware analysis from menu
+ private async void AnalyzeFirmware_Click(object sender, RoutedEventArgs e)
+ {
+ var dlg = new OpenFileDialog
+ {
+ Filter = "Firmware Archives (*.zip;*.tar;*.tar.gz;*.tar.bz2;*.bin)|*.zip;*.tar;*.tar.gz;*.tar.bz2;*.bin|All Files (*.*)|*.*"
+ };
+ if (dlg.ShowDialog() != true) return;
+ string archivePath = dlg.FileName;
+ string extractDir = Path.Combine(Path.GetDirectoryName(archivePath), Path.GetFileNameWithoutExtension(archivePath) + "_analyzed");
+ StatusBarText($"Analyzing firmware: {Path.GetFileName(archivePath)}...");
+ try
+ {
+ await Task.Run(() => FirmwareAnalyzer.AnalyzeFirmwareArchive(archivePath, extractDir));
+ StatusBarText("Firmware analysis complete.");
+ MessageBox.Show($"Firmware analysis finished. Check the console for details and extracted files at:\n{extractDir}", "Analysis Complete", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Firmware analysis failed: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ StatusBarText("Firmware analysis failed.");
+ }
+ }
+
+ // New handler to extract selected firmware archives
+ private async void ExtractFirmware_Click(object sender, RoutedEventArgs e)
+ {
+ var dlg = new OpenFileDialog
+ {
+ Filter = "Firmware Archives (*.zip;*.tar;*.tar.gz;*.tar.bz2;*.bin)|*.zip;*.tar;*.tar.gz;*.tar.bz2;*.bin|All Files (*.*)|*.*"
+ };
+ if (dlg.ShowDialog() != true) return;
+ string archivePath = dlg.FileName;
+ string extractDir = Path.Combine(Path.GetDirectoryName(archivePath), Path.GetFileNameWithoutExtension(archivePath) + "_extracted");
+ try
+ {
+ await Task.Run(() => ArchiveExtractor.ExtractAndAnalyze(archivePath, extractDir));
+ MessageBox.Show($"Extraction complete. Files extracted to:\n{extractDir}", "Extraction Complete", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Extraction failed: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ // New handler to detect the type of selected file
+ private void DetectFileType_Click(object sender, RoutedEventArgs e)
+ {
+ var dlg = new OpenFileDialog { Filter = "All Files (*.*)|*.*" };
+ if (dlg.ShowDialog() != true) return;
+ string filePath = dlg.FileName;
+ try
+ {
+ byte[] data = File.ReadAllBytes(filePath);
+ string type = AnalyzeFileType(filePath, data);
+ MessageBox.Show($"Detected file type: {type}", "File Type Detection", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Detection failed: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
// Helper to analyze file type
private static string AnalyzeFileType(string filePath, byte[] binary)
{
@@ -599,6 +910,14 @@ public static string AnalyzeFirmware(string filePath)
}
}
+ // Helper to parse DirecTV firmware filename metadata
+ private (string Manufacturer, string Model, string Version, string Tar)? ParseDirecTVFilename(string fileName)
+ {
+ var m = Regex.Match(fileName, @"image_mfr-(\d+)_mdl-([0-9a-z]+)_ver-(\d+)_tar-([0-9a-f]+)\\.csw", RegexOptions.IgnoreCase);
+ if (!m.Success) return null;
+ return (m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value, m.Groups[4].Value);
+ }
+
///
/// Simulates DirecTV SWM switches and/or LNBs.
/// Currently a stub; intended for future simulation and testing.
@@ -636,6 +955,66 @@ private async Task HandleSwmLnbSimulation()
await Task.CompletedTask;
}
+ // Handler for dropped files
+ private void MainWindow_Drop(object sender, DragEventArgs e)
+ {
+ if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
+ var files = (string[])e.Data.GetData(DataFormats.FileDrop);
+ foreach (var file in files)
+ {
+ string ext = Path.GetExtension(file).ToLowerInvariant();
+ // If unsupported extension, prompt to open GitHub issue
+ var supported = new[] { ".exe", ".dll", ".bin", ".csw", ".tar", ".img", ".fw" };
+ if (!supported.Contains(ext))
+ {
+ var ask = MessageBox.Show($"No handler for '{ext}'. Create an issue on GitHub?", "Unsupported File", MessageBoxButton.YesNo, MessageBoxImage.Question);
+ if (ask == MessageBoxResult.Yes)
+ Process.Start(new ProcessStartInfo("cmd", $"/c start https://github.com/julerobb1/Processor-Emulator/issues/new") { CreateNoWindow = true });
+ }
+ else
+ {
+ StatusBarText($"File dropped: {Path.GetFileName(file)}. Use menu to analyze.");
+ }
+ }
+ }
+
// All duplicate methods/helpers have been removed for clarity.
+
+ // New method to prompt user for QEMU options
+ private string PromptForQemuOptions()
+ {
+ var dialog = new Window
+ {
+ Title = "QEMU Options",
+ Width = 500,
+ Height = 300,
+ WindowStartupLocation = WindowStartupLocation.CenterOwner,
+ ResizeMode = ResizeMode.CanResize,
+ Owner = this
+ };
+ var stack = new StackPanel { Margin = new Thickness(10) };
+ stack.Children.Add(new TextBlock { Text = "Enter extra QEMU command-line options:", Margin = new Thickness(0, 0, 0, 5) });
+ var textBox = new TextBox
+ {
+ AcceptsReturn = true,
+ Text = string.Empty,
+ VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
+ HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
+ Height = 180
+ };
+ stack.Children.Add(textBox);
+ var btnPanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(0, 10, 0, 0) };
+ var okButton = new Button { Content = "OK", Width = 80, IsDefault = true, Margin = new Thickness(0, 0, 5, 0) };
+ var cancelButton = new Button { Content = "Cancel", Width = 80, IsCancel = true };
+ btnPanel.Children.Add(okButton);
+ btnPanel.Children.Add(cancelButton);
+ stack.Children.Add(btnPanel);
+ dialog.Content = stack;
+ string result = null;
+ okButton.Click += (s, e) => { result = textBox.Text.Trim(); dialog.DialogResult = true; dialog.Close(); };
+ if (dialog.ShowDialog() == true)
+ return result;
+ return string.Empty;
+ }
}
}
\ No newline at end of file
diff --git a/MipsCpuEmulator.cs b/MipsCpuEmulator.cs
index eced39b8..9ab6c788 100644
--- a/MipsCpuEmulator.cs
+++ b/MipsCpuEmulator.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Windows;
namespace ProcessorEmulator.Emulation
diff --git a/ProcessorEmulator.csproj b/ProcessorEmulator.csproj
index 4230a0af..e77ff9dd 100644
--- a/ProcessorEmulator.csproj
+++ b/ProcessorEmulator.csproj
@@ -4,8 +4,15 @@
WinExe
net6.0-windows
true
+
+ true
ProcessorEmulator
ProcessorEmulator
-
+
+
+
+
+
+
diff --git a/QemuManager.cs b/QemuManager.cs
index e41a498b..3492a168 100644
--- a/QemuManager.cs
+++ b/QemuManager.cs
@@ -60,8 +60,19 @@ private static string GetQemuExecutable(string architecture)
private static string GetQemuArgs(string imagePath, string architecture)
{
- // Basic args, can be extended for more options
- return $"-m 256 -drive file=\"{imagePath}\",format=raw -nographic";
+ // Select machine type based on architecture: malta for MIPS, virt for ARM
+ string machine = null;
+ if (architecture.StartsWith("MIPS", StringComparison.OrdinalIgnoreCase)) machine = "malta";
+ else if (architecture.StartsWith("ARM", StringComparison.OrdinalIgnoreCase)) machine = "virt";
+ string machineArg = string.IsNullOrEmpty(machine) ? string.Empty : $"-machine {machine}";
+ // If this is a raw firmware blob, boot it as a kernel with serial console
+ var ext = Path.GetExtension(imagePath).ToLowerInvariant();
+ if (ext == ".bin")
+ {
+ return $"{machineArg} -m 256 -kernel \"{imagePath}\" -serial stdio";
+ }
+ // Otherwise boot from image drive with VGA display
+ return $"{machineArg} -m 256 -drive file=\"{imagePath}\",format=raw -boot order=c -vga std -display sdl";
}
public static void Launch(string imagePath, string architecture)
@@ -71,16 +82,16 @@ public static void Launch(string imagePath, string architecture)
throw new FileNotFoundException($"QEMU executable not found: {qemuExe}");
var args = GetQemuArgs(imagePath, architecture);
+ // Launch QEMU with GUI window
+ // Launch QEMU directly with GUI
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = qemuExe,
Arguments = args,
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true
+ UseShellExecute = true,
+ CreateNoWindow = false
}
};
process.Start();
@@ -93,16 +104,16 @@ public static void LaunchWithArgs(string imagePath, string architecture, string
throw new FileNotFoundException($"QEMU executable not found: {qemuExe}");
var args = GetQemuArgs(imagePath, architecture) + " " + extraArgs;
+ // Launch QEMU with GUI window and extra args
+ // Launch QEMU with extra args directly
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = qemuExe,
- Arguments = args,
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true
+ Arguments = args + " " + extraArgs,
+ UseShellExecute = true,
+ CreateNoWindow = false
}
};
process.Start();
diff --git a/RDKVEmulator.cs b/RDKVEmulator.cs
index e7f22fce..91122fb4 100644
--- a/RDKVEmulator.cs
+++ b/RDKVEmulator.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
namespace ProcessorEmulator.Emulation
{
diff --git a/README.md b/README.md
index b2a99424..51ebfe67 100644
--- a/README.md
+++ b/README.md
@@ -5,33 +5,46 @@ Supports generic CPU/OS emulation, RDK-V/B, Uverse, DirecTV firmware analysis, f
## Features
-- **Generic CPU/OS Emulation:**
Load and emulate binaries for a wide range of architectures (MIPS, ARM, x86, PowerPC, SPARC, etc.) using QEMU or custom emulators.
-- **RDK-V Emulator:**
(Planned) Emulate RDK-V set-top box environments for research and reverse engineering.
-- **RDK-B Emulator:**
(Planned) Emulate RDK-B broadband gateway environments.
-- **Simulate SWM Switch/LNB:**
(Planned) Simulate DirecTV SWM switches and LNBs for testing and analysis.
-- **Probe Filesystem:**
Analyze and identify the type of filesystem in a given image or partition.
-- **Emulate CMTS Head End:**
(Planned) Simulate a Cable Modem Termination System (CMTS) head end for DOCSIS research.
-- **Uverse Box Emulator:**
(Planned) Emulate AT&T Uverse set-top boxes, including WinCE-based environments.
-- **DirecTV Box/Firmware Analysis:**
Analyze DirecTV firmware images for structure, content, and possible vulnerabilities.
-- **Linux Filesystem Read/Write:**
(Planned) Read and write to Linux filesystems (ext2/3/4, JFFS2, UBIFS, etc.) from Windows.
+
+## Cross-Compile Binary (RetDec Integration)
+
+This tool uses the [RetDec decompiler](https://github.com/avast/retdec) CLI to perform static cross-architecture translation of raw binaries.
+
+Prerequisites:
+1. Install RetDec on your system and ensure `retdec-decompiler` is in your PATH.
+ - Windows: Download the latest [RetDec Windows release](https://github.com/avast/retdec/releases) and add its `bin` folder to your PATH.
+ - Linux/macOS: Follow RetDec build instructions or use package manager if available.
+2. Supported source/target architectures: x86 (32-bit), x64 (64-bit), ARM, ARM64, MIPS.
+
+Usage:
+1. In the app UI, select **Cross-Compile Binary**.
+2. Choose an input file (`.bin`, `.exe`, etc.).
+3. Pick the target architecture from the dropdown.
+4. The tool will invoke:
+ ```
+ retdec-decompiler --mode raw -e -t -o