From 76372691f88246bf2ad3213c01c877a033012dd5 Mon Sep 17 00:00:00 2001
From: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com>
Date: Thu, 18 Sep 2025 16:26:11 -0700
Subject: [PATCH 1/8] create vcpkg.json
Signed-off-by: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com>
---
Bind/vcpkg.json | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 Bind/vcpkg.json
diff --git a/Bind/vcpkg.json b/Bind/vcpkg.json
new file mode 100644
index 0000000..fbad6bd
--- /dev/null
+++ b/Bind/vcpkg.json
@@ -0,0 +1,9 @@
+{
+ "name": "bind",
+ "version": "1.3.1",
+ "description": "SysCaller: Bind",
+ "dependencies": [
+ "cmark",
+ "pe-parse"
+ ]
+ }
From 39e4084cad4fc0c09423d90d576d1bfba23de4ee Mon Sep 17 00:00:00 2001
From: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com>
Date: Thu, 18 Sep 2025 16:26:48 -0700
Subject: [PATCH 2/8] update vcxproj for GH actions
Signed-off-by: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com>
---
Bind/Bind.vcxproj | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/Bind/Bind.vcxproj b/Bind/Bind.vcxproj
index 9a6020f..80080bb 100644
--- a/Bind/Bind.vcxproj
+++ b/Bind/Bind.vcxproj
@@ -58,17 +58,17 @@
- C:\Qt\5.15.2\msvc2019_64
+ $(QTDIR)
- C:\Qt\5.15.2\msvc2019_64
+ $(QTDIR)
- C:\Qt\5.15.2\msvc2019_64
+ $(QTDIR)
debug
- C:\Qt\5.15.2\msvc2019_64
+ $(QTDIR)
@@ -135,14 +135,14 @@
_DEBUG;UNICODE;_UNICODE;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_DLL;%(PreprocessorDefinitions)
true
stdcpp20
- GeneratedFiles\$(ConfigurationName);GeneratedFiles;C:\Qt\5.15.2\msvc2019_64\include;C:\Qt\5.15.2\msvc2019_64\include\QtCore;C:\Qt\5.15.2\msvc2019_64\include\QtGui;C:\Qt\5.15.2\msvc2019_64\include\QtWidgets;C:\Users\devil\vcpkg\installed\x64-windows\include;C:\Users\devil\source\repos\SysCaller\Bind\include;%(AdditionalIncludeDirectories)
+ GeneratedFiles\$(ConfigurationName);GeneratedFiles;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(VcpkgRoot)\installed\x64-windows\include;$(ProjectDir);%(AdditionalIncludeDirectories)
true
Windows
true
- C:\Qt\5.15.2\msvc2019_64\lib;C:\Users\devil\vcpkg\installed\x64-windows\lib;%(AdditionalLibraryDirectories)
- Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;cmark.lib;pe-parse.lib;%(AdditionalDependencies)
+ $(QTDIR)\lib;$(VcpkgRoot)\installed\x64-windows\lib;$(ProjectDir);%(AdditionalLibraryDirectories)
+ qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;cmark.lib;pe-parse.lib;%(AdditionalDependencies)
@@ -154,14 +154,14 @@
NDEBUG;UNICODE;_UNICODE;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_DLL;%(PreprocessorDefinitions)
true
stdcpp20
- GeneratedFiles\$(ConfigurationName);GeneratedFiles;C:\Qt\5.15.2\msvc2019_64\include;C:\Qt\5.15.2\msvc2019_64\include\QtCore;C:\Qt\5.15.2\msvc2019_64\include\QtGui;C:\Qt\5.15.2\msvc2019_64\include\QtWidgets;C:\Users\devil\vcpkg\installed\x64-windows\include;C:\Users\devil\source\repos\SysCaller\Bind\include;%(AdditionalIncludeDirectories)
+ GeneratedFiles\$(ConfigurationName);GeneratedFiles;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(VcpkgRoot)\installed\x64-windows\include;$(ProjectDir);%(AdditionalIncludeDirectories)
true
Windows
true
- C:\Qt\5.15.2\msvc2019_64\lib;C:\Users\devil\vcpkg\installed\x64-windows\lib;%(AdditionalLibraryDirectories)
- Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;capstone.lib;pe-parse.lib;cmark.lib;%(AdditionalDependencies)
+ $(QTDIR)\lib;$(VcpkgRoot)\installed\x64-windows\lib;$(ProjectDir);%(AdditionalLibraryDirectories)
+ qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;cmark.lib;pe-parse.lib;%(AdditionalDependencies)
@@ -243,6 +243,7 @@
+
@@ -460,4 +461,3 @@
-
From f25b80fa6ae1d8c93b94ce5423dbe0accc8a7d32 Mon Sep 17 00:00:00 2001
From: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com>
Date: Thu, 18 Sep 2025 16:27:48 -0700
Subject: [PATCH 3/8] create build.yml
Signed-off-by: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com>
---
.github/workflows/build.yml | 430 ++++++++++++++++++++++++++++++++++++
1 file changed, 430 insertions(+)
create mode 100644 .github/workflows/build.yml
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..a60f916
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,430 @@
+name: Build Bind (SysCaller)
+
+on:
+ push:
+ branches: [ main, master ]
+ pull_request:
+ branches: [ main, master ]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: windows-latest
+
+ defaults:
+ run:
+ working-directory: ./Bind
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v1.3
+
+ - name: Setup vcpkg
+ uses: lukka/run-vcpkg@v11
+ with:
+ vcpkgDirectory: '${{ github.workspace }}/vcpkg'
+ vcpkgGitCommitId: '7e19f3c64cb636ee21f41bfe8558a6dfaae6236f'
+ vcpkgJsonGlob: 'vcpkg.json'
+ runVcpkgInstall: '--triplet=x64-windows --x-wait-for-lock'
+
+ - name: Set Vcpkg Environment
+ run: |
+ echo "VCPKG_ROOT=${{ github.workspace }}/vcpkg" >> $env:GITHUB_ENV
+ echo "VcpkgRoot=${{ github.workspace }}/vcpkg" >> $env:GITHUB_ENV
+ echo "VCPKG_DEFAULT_TRIPLET=x64-windows" >> $env:GITHUB_ENV
+ echo "CMAKE_WARN_UNUSED_CLI_VARS=OFF" >> $env:GITHUB_ENV
+ echo "VCPKG_CMAKE_CONFIGURE_OPTIONS=-DCMAKE_WARN_UNUSED_CLI_VARS=OFF" >> $env:GITHUB_ENV
+
+ - name: Setup Vcpkg Binary Caching
+ uses: actions/cache@v4
+ with:
+ path: |
+ ${{ github.workspace }}/vcpkg/installed
+ ${{ github.workspace }}/vcpkg/packages
+ key: vcpkg-${{ hashFiles('**/vcpkg.json') }}-${{ runner.os }}-x64-windows
+ restore-keys: |
+ vcpkg-${{ hashFiles('**/vcpkg.json') }}-${{ runner.os }}-
+ vcpkg-${{ runner.os }}-
+
+ - name: Integrate Vcpkg with MSBuild
+ run: |
+ & "${{ github.workspace }}/vcpkg/vcpkg.exe" integrate install
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v4
+ with:
+ version: '5.15.2'
+ host: 'windows'
+ target: 'desktop'
+ arch: 'win64_msvc2019_64'
+ cache: true
+
+ - name: Set Qt Environment Variables
+ run: |
+ # The Qt action installs to D:\a\SysCaller\Qt, not in the workspace
+ $qtPath = "D:\a\SysCaller\Qt\5.15.2\msvc2019_64"
+ echo "QTDIR=$qtPath" >> $env:GITHUB_ENV
+ echo "QT_ROOT_DIR=$qtPath" >> $env:GITHUB_ENV
+ echo "QT_PLUGIN_PATH=$qtPath\plugins" >> $env:GITHUB_ENV
+ echo "QML2_IMPORT_PATH=$qtPath\qml" >> $env:GITHUB_ENV
+ echo "PATH=$qtPath\bin;$env:PATH" >> $env:GITHUB_ENV
+
+ # Verify Qt Tools are Available
+ Write-Host "Checking Qt Tools Availability:"
+ Write-Host "Looking for Qt at: $qtPath"
+ if (Test-Path "$qtPath\bin\moc.exe") {
+ Write-Host "moc.exe found"
+ & "$qtPath\bin\moc.exe" -v
+ } else {
+ Write-Host "moc.exe not found, looking for alternatives..."
+ Get-ChildItem "D:\a\SysCaller\Qt" -Recurse -Name "moc.exe" | ForEach-Object { Write-Host "Found: $_" }
+ }
+
+ if (Test-Path "$qtPath\bin\rcc.exe") {
+ Write-Host "rcc.exe found"
+ } else {
+ Write-Host "rcc.exe not found"
+ }
+
+ - name: Setup Qt MSBuild Integration
+ run: |
+ # Create QtMsBuild Directory for Basic Qt Integration
+ $qtPath = "D:\a\SysCaller\Qt\5.15.2\msvc2019_64"
+ $qtMsBuildPath = "QtMsBuild"
+
+ # Create QtMsBuild Directory
+ New-Item -ItemType Directory -Path $qtMsBuildPath -Force
+
+ # Create Qt.props File
+ $qtPropsContent = '' + "`n" +
+ '' + "`n" +
+ ' ' + "`n" +
+ " $qtPath" + "`n" +
+ " 5.15.2" + "`n" +
+ " true" + "`n" +
+ ' ' + "`n" +
+ ''
+ $qtPropsContent | Out-File -FilePath "$qtMsBuildPath\Qt.props" -Encoding UTF8
+
+ # Create qt_defaults.props File
+ $qtDefaultsContent = '' + "`n" +
+ '' + "`n" +
+ ' ' + "`n" +
+ " $qtPath" + "`n" +
+ " 5.15.2" + "`n" +
+ ' ' + "`n" +
+ ''
+ $qtDefaultsContent | Out-File -FilePath "$qtMsBuildPath\qt_defaults.props" -Encoding UTF8
+
+ # Create qt.targets File with RCC support
+ $qtTargetsContent = '' + "`n" +
+ '' + "`n" +
+ ' ' + "`n" +
+ " $qtPath" + "`n" +
+ " 5.15.2" + "`n" +
+ ' ' + "`n" +
+ ' ' + "`n" +
+ ' ' + "`n" +
+ ' ' + "`n" +
+ ' ' + "`n" +
+ ' ' + "`n" +
+ ''
+ $qtTargetsContent | Out-File -FilePath "$qtMsBuildPath\qt.targets" -Encoding UTF8
+
+ # Set QtMsBuild Environment Variable
+ echo "QtMsBuild=$qtMsBuildPath" >> $env:GITHUB_ENV
+ Write-Host "QtMsBuild Set to: $qtMsBuildPath"
+
+ - name: Pre-generate MOC Files
+ run: |
+ $qtPath = "D:\a\SysCaller\Qt\5.15.2\msvc2019_64"
+ $mocPath = "$qtPath\bin\moc.exe"
+ $rccPath = "$qtPath\bin\rcc.exe"
+
+ if (Test-Path $mocPath) {
+ Write-Host "Pre-generating MOC Files..."
+
+ # Create GeneratedFiles Directory if it doesn't exist
+ New-Item -ItemType Directory -Path "GeneratedFiles" -Force
+
+ # List of Header Files that need MOC Generation
+ $mocFiles = @(
+ "include/GUI/MainWindow.h",
+ "include/GUI/Bars/ProgressBar.h",
+ "include/GUI/Bars/StatusBar.h",
+ "include/GUI/Bars/TitleBar.h",
+ "include/GUI/Bars/SettingsTitleBar.h",
+ "include/GUI/Buttons/BindButton.h",
+ "include/GUI/Panels/LeftPanel.h",
+ "include/GUI/Panels/OutputPanel.h",
+ "include/GUI/Panels/RightPanel.h",
+ "include/GUI/Dialogs/ChangelogDialog.h",
+ "include/GUI/Dialogs/HashCompareDialog.h",
+ "include/GUI/Dialogs/ObfuscationSelectionDialog.h",
+ "include/GUI/Dialogs/StubMapperDialog.h",
+ "include/GUI/Dialogs/SettingsDialog.h",
+ "include/GUI/Dialogs/ConfirmationDialog.h",
+ "include/GUI/Settings/Tabs/GeneralTab.h",
+ "include/GUI/Settings/Tabs/ObfuscationTab.h",
+ "include/GUI/Settings/Tabs/IntegrityTab.h",
+ "include/GUI/Settings/Tabs/ProfileTab.h",
+ "include/GUI/Settings/Tabs/IndirectObfuscationTab.h",
+ "include/GUI/Settings/Tabs/InlineObfuscationTab.h",
+ "include/GUI/Threads/ValidatorThread.h",
+ "include/GUI/Threads/CompatibilityThread.h",
+ "include/GUI/Threads/VerificationThread.h",
+ "include/GUI/Threads/ObfuscationThread.h",
+ "include/Core/Integrity/Compatibility/Compatibility.h",
+ "include/Core/Integrity/Validator/Validator.h",
+ "include/Core/Integrity/Verification/Verification.h"
+ )
+
+ foreach ($headerFile in $mocFiles) {
+ if (Test-Path $headerFile) {
+ $outputFile = "GeneratedFiles/moc_" + [System.IO.Path]::GetFileNameWithoutExtension($headerFile) + ".cpp"
+ Write-Host "Generating MOC for: $headerFile -> $outputFile"
+ & $mocPath $headerFile -o $outputFile
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "Successfully generated: $outputFile"
+ } else {
+ Write-Host "MOC generation failed for $headerFile with exit code: $LASTEXITCODE"
+ }
+ } else {
+ Write-Host "Header file not found: $headerFile"
+ }
+ }
+
+ # Generate RCC file manually
+ if (Test-Path $rccPath) {
+ Write-Host "Generating RCC file..."
+ $rccOutput = "GeneratedFiles/qrc_resources.cpp"
+ & $rccPath -name resources "resources.qrc" -o $rccOutput
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "Successfully generated RCC file: $rccOutput"
+ } else {
+ Write-Host "RCC generation failed with exit code: $LASTEXITCODE"
+ }
+ } else {
+ Write-Host "RCC tool not found, skipping resource compilation"
+ }
+
+ Write-Host "MOC generation completed"
+ } else {
+ Write-Host "MOC tool not found, skipping pre-generation"
+ }
+
+ - name: Verify Qt Installation
+ run: |
+ Write-Host "Qt Installation Verification:"
+ Write-Host "QTDIR: $env:QTDIR"
+ if (Test-Path $env:QTDIR) {
+ Write-Host "Qt directory exists"
+ Get-ChildItem $env:QTDIR | Select-Object Name
+ } else {
+ Write-Host "Qt Directory not found!"
+ }
+
+ if (Test-Path "$env:QTDIR\bin") {
+ Write-Host "Qt bin Directory exists"
+ Get-ChildItem "$env:QTDIR\bin" | Where-Object { $_.Name -like "qmake*" } | Select-Object Name
+ } else {
+ Write-Host "Qt bin Directory not found!"
+ }
+
+ - name: Verify Generated Files
+ run: |
+ Write-Host "Checking Generated Files:"
+ if (Test-Path "GeneratedFiles") {
+ Write-Host "GeneratedFiles directory exists"
+ Get-ChildItem "GeneratedFiles" | Select-Object Name, Length
+
+ if (Test-Path "GeneratedFiles/qrc_resources.cpp") {
+ Write-Host "RCC file generated successfully!"
+ $rccSize = (Get-Item "GeneratedFiles/qrc_resources.cpp").Length
+ Write-Host "RCC file size: $rccSize bytes"
+ } else {
+ Write-Host "RCC file NOT found!"
+ }
+ } else {
+ Write-Host "GeneratedFiles directory not found!"
+ }
+
+ - name: Build Solution (Debug x64)
+ run: |
+ # Build with proper Environment Variables
+ Write-Host "Building Debug x64..."
+ Write-Host "QTDIR: $env:QTDIR"
+ Write-Host "VcpkgRoot: $env:VcpkgRoot"
+ Write-Host "VCPKG_ROOT: $env:VCPKG_ROOT"
+
+ msbuild "Bind.sln" /p:Configuration=Debug /p:Platform=x64 /p:VcpkgEnabled=true /p:VcpkgEnableManifest=true /p:VcpkgUseStatic=false /p:VcpkgTriplet=x64-windows /p:QTDIR="$env:QTDIR" /p:VcpkgRoot="$env:VcpkgRoot"
+
+ - name: Copy Vcpkg Dependencies (Debug)
+ run: |
+ Write-Host "Copying Vcpkg Dependencies for Debug Build..."
+ $outputDir = "x64/Debug"
+ $vcpkgBin = "${{ github.workspace }}/vcpkg/installed/x64-windows/bin"
+
+ # Copy Vcpkg Dependencies (Qt dependencies will be handled by windeployqt)
+ if (Test-Path "$vcpkgBin/pe-parsed.dll") {
+ Copy-Item "$vcpkgBin/pe-parsed.dll" $outputDir
+ Write-Host "Copied pe-parsed.dll"
+ }
+ if (Test-Path "$vcpkgBin/cmark.dll") {
+ Copy-Item "$vcpkgBin/cmark.dll" $outputDir
+ Write-Host "Copied cmark.dll"
+ }
+
+ Write-Host "Vcpkg dependencies copied to $outputDir"
+
+ - name: Deploy Qt Dependencies (Debug)
+ run: |
+ Write-Host "Running Qt deployment for Debug build..."
+ $qtBin = "D:\a\SysCaller\Qt\5.15.2\msvc2019_64\bin"
+ $windeployqt = "$qtBin\windeployqt.exe"
+
+ if (Test-Path $windeployqt) {
+ Write-Host "Running windeployqt on Debug executable..."
+ Write-Host "Checking GeneratedFiles directory before windeployqt..."
+ if (Test-Path "GeneratedFiles") {
+ Get-ChildItem "GeneratedFiles" -Name | Where-Object { $_ -like "*qrc*" } | ForEach-Object { Write-Host "Found RCC file: $_" }
+ }
+
+ & $windeployqt "x64/Debug/Bind.exe" --debug --no-compiler-runtime --no-opengl-sw --force
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "Qt deployment completed successfully for Debug build"
+ Get-ChildItem "x64/Debug" -Name | Sort-Object
+ } else {
+ Write-Host "windeployqt failed with exit code: $LASTEXITCODE"
+ }
+ } else {
+ Write-Host "windeployqt.exe not found at: $windeployqt"
+ }
+
+ - name: Build Solution (Release x64)
+ run: |
+ # Build with proper Environment Variables
+ Write-Host "Building Release x64..."
+ Write-Host "QTDIR: $env:QTDIR"
+ Write-Host "VcpkgRoot: $env:VcpkgRoot"
+ Write-Host "VCPKG_ROOT: $env:VCPKG_ROOT"
+
+ msbuild "Bind.sln" /p:Configuration=Release /p:Platform=x64 /p:VcpkgEnabled=true /p:VcpkgEnableManifest=true /p:VcpkgUseStatic=false /p:VcpkgTriplet=x64-windows /p:QTDIR="$env:QTDIR" /p:VcpkgRoot="$env:VcpkgRoot"
+
+ - name: Copy Vcpkg Dependencies (Release)
+ run: |
+ Write-Host "Copying Vcpkg Dependencies for Release Build..."
+ $outputDir = "x64/Release"
+ $vcpkgBin = "${{ github.workspace }}/vcpkg/installed/x64-windows/bin"
+
+ # Copy Vcpkg Dependencies (Qt dependencies will be handled by windeployqt)
+ if (Test-Path "$vcpkgBin/pe-parse.dll") {
+ Copy-Item "$vcpkgBin/pe-parse.dll" $outputDir
+ Write-Host "Copied pe-parse.dll"
+ }
+ if (Test-Path "$vcpkgBin/cmark.dll") {
+ Copy-Item "$vcpkgBin/cmark.dll" $outputDir
+ Write-Host "Copied cmark.dll"
+ }
+
+ Write-Host "Vcpkg dependencies copied to $outputDir"
+
+ - name: Deploy Qt Dependencies (Release)
+ run: |
+ Write-Host "Running Qt deployment for Release build..."
+ $qtBin = "D:\a\SysCaller\Qt\5.15.2\msvc2019_64\bin"
+ $windeployqt = "$qtBin\windeployqt.exe"
+
+ if (Test-Path $windeployqt) {
+ Write-Host "Running windeployqt on Release executable..."
+ & $windeployqt "x64/Release/Bind.exe" --release --no-compiler-runtime --no-opengl-sw --force
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "Qt deployment completed successfully for Release build"
+ Get-ChildItem "x64/Release" -Name | Sort-Object
+ } else {
+ Write-Host "windeployqt failed with exit code: $LASTEXITCODE"
+ }
+ } else {
+ Write-Host "windeployqt.exe not found at: $windeployqt"
+ }
+
+ - name: Verify Executables Exist
+ run: |
+ if (Test-Path "x64/Release/Bind.exe") {
+ Write-Host "Bind.exe (Release) Built Successfully!"
+ Get-Item "x64/Release/Bind.exe" | Select-Object Name, Length, LastWriteTime
+ } else {
+ Write-Host "Bind.exe (Release) not found!"
+ Get-ChildItem -Recurse -Name "*.exe" | ForEach-Object { Write-Host "Found: $_" }
+ exit 1
+ }
+
+ if (Test-Path "x64/Debug/Bind.exe") {
+ Write-Host "Bind.exe (Debug) Built Successfully!"
+ Get-Item "x64/Debug/Bind.exe" | Select-Object Name, Length, LastWriteTime
+ } else {
+ Write-Host "Bind.exe (Debug) not found!"
+ }
+
+ - name: List Build Directory Contents
+ run: |
+ Write-Host "Current Directory Contents:"
+ Get-ChildItem -Recurse | Where-Object { $_.Name -like "*.exe" -or $_.Name -like "*.pdb" -or $_.Name -like "*.zip" } | Select-Object FullName, Length, LastWriteTime
+
+ - name: Upload Build Artifacts (Release)
+ uses: actions/upload-artifact@v4
+ with:
+ name: Bind-x64-Release
+ path: Bind/x64/Release/
+ retention-days: 30
+
+ - name: Upload Build Artifacts (Debug)
+ uses: actions/upload-artifact@v4
+ with:
+ name: Bind-x64-Debug
+ path: Bind/x64/Debug/
+ retention-days: 30
+
+ - name: Create Release Package
+ if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
+ run: |
+ $version = Get-Date -Format "yyyy.MM.dd-HHmm"
+ $zipName = "Bind-x64-$version.zip"
+
+ # Create a temporary dir for packaging
+ New-Item -ItemType Directory -Path "release-package" -Force
+
+ # Copy the entire Release directory (includes all dependencies from windeployqt + vcpkg)
+ Copy-Item "x64/Release/*" "release-package\" -Recurse
+
+ # Create README
+ $readmeText = "# Bind (SysCaller) - x64`n`n"
+ $readmeText += "Build Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC')`n"
+ $readmeText += "Commit: ${{ github.sha }}`n"
+ $readmeText += "Platform: Windows x64 (64-bit)`n`n"
+ $readmeText += "## Installation`n"
+ $readmeText += "1. Download Bind.exe`n"
+ $readmeText += "2. Run the executable`n`n"
+ $readmeText += "## Requirements`n"
+ $readmeText += "- Windows 10 or later`n"
+ $readmeText += "- Visual C++ Redistributable (if not already installed)`n"
+ $readmeText += "- Qt5 runtime libraries (if not already installed)"
+ $readmeText | Out-File -FilePath "release-package\README.txt" -Encoding UTF8
+
+ # Create the zip file
+ Compress-Archive -Path "release-package\*" -DestinationPath $zipName -Force
+
+ Write-Host "Created Release Package: $zipName"
+ Get-Item $zipName | Select-Object Name, Length
+
+ - name: Upload Release Package
+ if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
+ uses: actions/upload-artifact@v4
+ with:
+ name: Release-Package-x64
+ path: Bind/Bind-x64-*.zip
+ retention-days: 90
From 4668b5bd447b08cacdd446e400d1965112876e3d Mon Sep 17 00:00:00 2001
From: WindowsAPI
Date: Thu, 25 Sep 2025 21:18:39 -0700
Subject: [PATCH 4/8] add enums and refactor obfuscation and integrity modules
---
Bind/Bind.vcxproj | 38 +-
.../Direct/Encryption/DirectEncryptor.h | 30 +-
.../ControlFlow/IndirectControlFlow.h | 25 +-
.../Indirect/Stub/IndirectStubGenerator.h | 24 +
Bind/include/Core/Obfuscation/Obfuscation.h | 20 +
Bind/resources.qrc | 18 +-
.../Integrity/Compatibility/Compatibility.cpp | 352 +++++---
.../Core/Integrity/Validator/Validator.cpp | 779 +++++++++++++-----
.../Integrity/Verification/Verification.cpp | 558 ++++++++++---
.../Direct/ControlFlow/DirectControlFlow.cpp | 125 ++-
.../Direct/Encryption/DirectEncryptor.cpp | 101 ++-
.../Direct/Mapping/DirectStubMapper.cpp | 609 ++++++++++----
.../Direct/Stub/DirectJunkGenerator.cpp | 69 +-
.../Direct/Stub/DirectStubGenerator.cpp | 83 +-
.../ControlFlow/IndirectControlFlow.cpp | 37 +-
.../Indirect/Encryption/IndirectEncryptor.cpp | 12 +-
.../Indirect/Stub/IndirectJunkGenerator.cpp | 28 +-
.../Indirect/Stub/IndirectStubGenerator.cpp | 48 +-
.../Core/Obfuscation/IndirectObfuscation.cpp | 387 ++++++---
Bind/src/Core/Obfuscation/Obfuscation.cpp | 602 ++++++++++----
.../Obfuscation/Shared/Stub/NameGenerator.cpp | 75 +-
Bind/src/Core/Utils/PathUtils.cpp | 129 ++-
Bind/src/Core/Utils/Utils.cpp | 393 +++++++--
Bind/src/GUI/Bars/SettingsTitleBar.cpp | 34 +-
Bind/src/GUI/Bars/StatusBar.cpp | 60 +-
Bind/src/GUI/Bars/TitleBar.cpp | 45 +-
Bind/src/GUI/Buttons/BindButton.cpp | 54 +-
Bind/src/GUI/Dialogs/ChangelogDialog.cpp | 92 ++-
Bind/src/GUI/Dialogs/ConfirmationDialog.cpp | 79 +-
Bind/src/GUI/Dialogs/HashCompareDialog.cpp | 519 +++++++++---
.../Dialogs/ObfuscationSelectionDialog.cpp | 30 +-
Bind/src/GUI/Dialogs/SettingsDialog.cpp | 100 ++-
Bind/src/GUI/Dialogs/StubMapperDialog.cpp | 334 ++++++--
Bind/src/GUI/MainWindow.cpp | 281 +++++--
Bind/src/GUI/Panels/LeftPanel.cpp | 123 ++-
Bind/src/GUI/Panels/OutputPanel.cpp | 10 +-
Bind/src/GUI/Panels/RightPanel.cpp | 13 +-
Bind/src/GUI/Settings/Tabs/GeneralTab.cpp | 489 ++++++++---
.../Settings/Tabs/IndirectObfuscationTab.cpp | 74 +-
.../Settings/Tabs/InlineObfuscationTab.cpp | 17 +-
Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp | 130 ++-
Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp | 71 +-
Bind/src/GUI/Settings/Tabs/ProfileTab.cpp | 82 +-
Bind/src/GUI/Threads/CompatibilityThread.cpp | 40 +-
Bind/src/GUI/Threads/ObfuscationThread.cpp | 66 +-
Bind/src/GUI/Threads/ValidatorThread.cpp | 42 +-
Bind/src/GUI/Threads/VerificationThread.cpp | 25 +-
Bind/src/Res/Icons/compatibility.png | Bin 0 -> 921 bytes
Bind/src/Res/Icons/compatibility.svg | 1 -
Bind/src/Res/Icons/export.png | Bin 0 -> 628 bytes
Bind/src/Res/Icons/export.svg | 1 -
Bind/src/Res/Icons/obfuscation.png | Bin 0 -> 614 bytes
Bind/src/Res/Icons/obfuscation.svg | 1 -
Bind/src/Res/Icons/refresh.png | Bin 0 -> 835 bytes
Bind/src/Res/Icons/refresh.svg | 1 -
Bind/src/Res/Icons/settings.png | Bin 0 -> 810 bytes
Bind/src/Res/Icons/settings.svg | 1 -
Bind/src/Res/Icons/validation.png | Bin 0 -> 724 bytes
Bind/src/Res/Icons/validation.svg | 1 -
Bind/src/Res/Icons/verification.png | Bin 0 -> 773 bytes
Bind/src/Res/Icons/verification.svg | 1 -
61 files changed, 5443 insertions(+), 1916 deletions(-)
create mode 100644 Bind/src/Res/Icons/compatibility.png
delete mode 100644 Bind/src/Res/Icons/compatibility.svg
create mode 100644 Bind/src/Res/Icons/export.png
delete mode 100644 Bind/src/Res/Icons/export.svg
create mode 100644 Bind/src/Res/Icons/obfuscation.png
delete mode 100644 Bind/src/Res/Icons/obfuscation.svg
create mode 100644 Bind/src/Res/Icons/refresh.png
delete mode 100644 Bind/src/Res/Icons/refresh.svg
create mode 100644 Bind/src/Res/Icons/settings.png
delete mode 100644 Bind/src/Res/Icons/settings.svg
create mode 100644 Bind/src/Res/Icons/validation.png
delete mode 100644 Bind/src/Res/Icons/validation.svg
create mode 100644 Bind/src/Res/Icons/verification.png
delete mode 100644 Bind/src/Res/Icons/verification.svg
diff --git a/Bind/Bind.vcxproj b/Bind/Bind.vcxproj
index 9a6020f..ffd332a 100644
--- a/Bind/Bind.vcxproj
+++ b/Bind/Bind.vcxproj
@@ -1,4 +1,4 @@
-
+
@@ -69,6 +69,7 @@
C:\Qt\5.15.2\msvc2019_64
+ 5.15.2_msvc2019_64
@@ -172,6 +173,15 @@
+
+
+
+
+
+
+
+
+
@@ -215,15 +225,6 @@
-
-
-
-
-
-
-
-
-
@@ -415,15 +416,15 @@
-
-
+
+
-
-
-
+
+
+
-
-
+
+
@@ -459,5 +460,4 @@
-
-
+
\ No newline at end of file
diff --git a/Bind/include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h b/Bind/include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h
index 507ad2d..fa4498d 100644
--- a/Bind/include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h
+++ b/Bind/include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h
@@ -9,6 +9,34 @@
namespace DirectObfuscation {
+ enum class EncryptionMethod {
+ BasicXOR = 1, // simple XOR encryption
+ MultiKeyXOR = 2, // multi key XOR encryption
+ AddXORCombo = 3, // addition + XOR combination
+ EnhancedXOR = 4, // enhanced XOR with larger keys
+ OffsetShifting = 5 // offset shifting/masking
+ };
+
+ inline QString encryptionMethodToString(EncryptionMethod method) {
+ switch (method) {
+ case EncryptionMethod::BasicXOR: return "Basic XOR";
+ case EncryptionMethod::MultiKeyXOR: return "Multi Key XOR";
+ case EncryptionMethod::AddXORCombo: return "Add + XOR Combo";
+ case EncryptionMethod::EnhancedXOR: return "Enhanced XOR";
+ case EncryptionMethod::OffsetShifting: return "Offset Shifting";
+ default: return "Unknown";
+ }
+ }
+
+ inline EncryptionMethod stringToEncryptionMethod(const QString& str) {
+ if (str.contains("basic", Qt::CaseInsensitive)) return EncryptionMethod::BasicXOR;
+ if (str.contains("multi", Qt::CaseInsensitive)) return EncryptionMethod::MultiKeyXOR;
+ if (str.contains("add", Qt::CaseInsensitive) || str.contains("combo", Qt::CaseInsensitive)) return EncryptionMethod::AddXORCombo;
+ if (str.contains("enhanced", Qt::CaseInsensitive)) return EncryptionMethod::EnhancedXOR;
+ if (str.contains("offset", Qt::CaseInsensitive) || str.contains("shifting", Qt::CaseInsensitive)) return EncryptionMethod::OffsetShifting;
+ return EncryptionMethod::BasicXOR; // default
+ }
+
class Encryptor {
private:
QSettings* settings;
@@ -18,7 +46,7 @@ namespace DirectObfuscation {
public:
explicit Encryptor(QSettings* settings = nullptr);
- int getEncryptionMethod();
+ DirectObfuscation::EncryptionMethod getEncryptionMethod();
QPair> encryptOffset(int realOffset, int method = -1);
QStringList generateDecryptionSequence(const QString& offsetName,
const QMap& encryptionData,
diff --git a/Bind/include/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.h b/Bind/include/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.h
index 5852939..35786da 100644
--- a/Bind/include/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.h
+++ b/Bind/include/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.h
@@ -8,6 +8,30 @@
namespace IndirectObfuscation {
+ enum class ControlFlowPattern {
+ RegisterBased = 0, // register based opaque predicate
+ ValueBased = 1, // value based opaque predicate
+ FlagBased = 2, // flag based opaque predicate
+ MixedJunkCode = 3 // mixed junk code with opaque predicate
+ };
+
+ inline QString controlFlowPatternToString(ControlFlowPattern pattern) {
+ switch (pattern) {
+ case ControlFlowPattern::RegisterBased: return "register";
+ case ControlFlowPattern::ValueBased: return "value";
+ case ControlFlowPattern::FlagBased: return "flag";
+ case ControlFlowPattern::MixedJunkCode: return "mixed";
+ default: return "register";
+ }
+ }
+
+ inline ControlFlowPattern stringToControlFlowPattern(const QString& str) {
+ if (str == "value") return ControlFlowPattern::ValueBased;
+ if (str == "flag") return ControlFlowPattern::FlagBased;
+ if (str == "mixed") return ControlFlowPattern::MixedJunkCode;
+ return ControlFlowPattern::RegisterBased; // default
+ }
+
class ControlFlow {
private:
QSettings* settings;
@@ -15,7 +39,6 @@ namespace IndirectObfuscation {
public:
explicit ControlFlow(QSettings* settings);
- // Generates a control flow obfuscation pattern for indirect stubs
QString generateControlFlowObfuscation();
};
diff --git a/Bind/include/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.h b/Bind/include/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.h
index 9272d06..db2fc7d 100644
--- a/Bind/include/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.h
+++ b/Bind/include/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.h
@@ -7,6 +7,30 @@
namespace IndirectObfuscation {
+ enum class ResolverCallMethod {
+ RegisterPointer = 0, // Register pointer call via R10
+ StackIndirect = 1, // Stack indirect call (16 byte aligned)
+ StackScratch = 2, // Stack scratch space indirect call
+ RegisterShuffle = 3 // Register shuffle call via R10
+ };
+
+ inline QString resolverCallMethodToString(ResolverCallMethod method) {
+ switch (method) {
+ case ResolverCallMethod::RegisterPointer: return "register";
+ case ResolverCallMethod::StackIndirect: return "stack";
+ case ResolverCallMethod::StackScratch: return "indirect";
+ case ResolverCallMethod::RegisterShuffle: return "shuffle";
+ default: return "register";
+ }
+ }
+
+ inline ResolverCallMethod stringToResolverCallMethod(const QString& str) {
+ if (str == "stack") return ResolverCallMethod::StackIndirect;
+ if (str == "indirect") return ResolverCallMethod::StackScratch;
+ if (str == "shuffle") return ResolverCallMethod::RegisterShuffle;
+ return ResolverCallMethod::RegisterPointer; // default
+ }
+
class StubGenerator {
private:
QSettings* settings;
diff --git a/Bind/include/Core/Obfuscation/Obfuscation.h b/Bind/include/Core/Obfuscation/Obfuscation.h
index 8286525..181b57c 100644
--- a/Bind/include/Core/Obfuscation/Obfuscation.h
+++ b/Bind/include/Core/Obfuscation/Obfuscation.h
@@ -8,6 +8,26 @@
#include
#include "include/GUI/Themes/Colors.h"
+enum class ObfuscationMode {
+ Normal = 0,
+ StubMapper = 1
+};
+
+inline QString obfuscationModeToString(ObfuscationMode mode) {
+ switch (mode) {
+ case ObfuscationMode::Normal: return "Normal";
+ case ObfuscationMode::StubMapper: return "Stub Mapper";
+ default: return "Unknown";
+ }
+}
+
+inline ObfuscationMode stringToObfuscationMode(const QString& str) {
+ if (str == "stub_mapper" || str == "Stub Mapper") {
+ return ObfuscationMode::StubMapper;
+ }
+ return ObfuscationMode::Normal;
+}
+
class Obfuscation {
private:
std::function outputCallback;
diff --git a/Bind/resources.qrc b/Bind/resources.qrc
index 5fd4851..59dda9a 100644
--- a/Bind/resources.qrc
+++ b/Bind/resources.qrc
@@ -1,20 +1,20 @@
- src/Res/Icons/validation.svg
- src/Res/Icons/compatibility.svg
- src/Res/Icons/verification.svg
- src/Res/Icons/obfuscation.svg
- src/Res/Icons/settings.svg
+ src/Res/Icons/validation.png
+ src/Res/Icons/compatibility.png
+ src/Res/Icons/verification.png
+ src/Res/Icons/obfuscation.png
+ src/Res/Icons/settings.png
src/Res/Icons/syscaller.png
src/Res/Icons/logo.ico
- src/Res/Icons/export.svg
- src/Res/Icons/refresh.svg
+ src/Res/Icons/export.png
+ src/Res/Icons/refresh.png
src/Res/Fonts/ibmplexmono.ttf
src/GUI/Stylesheets/SettingsDialog.qss
src/GUI/Stylesheets/StubMapperDialog.qss
+ src/GUI/Stylesheets/ConfirmationDialog.qss
src/GUI/Stylesheets/ObfuscationSelectionDialog.qss
src/GUI/Stylesheets/HashCompareDialog.qss
src/GUI/Stylesheets/ChangelogDialog.qss
-src/GUI/Stylesheets/ConfirmationDialog.qss
-
+
\ No newline at end of file
diff --git a/Bind/src/Core/Integrity/Compatibility/Compatibility.cpp b/Bind/src/Core/Integrity/Compatibility/Compatibility.cpp
index 476909f..a82696d 100644
--- a/Bind/src/Core/Integrity/Compatibility/Compatibility.cpp
+++ b/Bind/src/Core/Integrity/Compatibility/Compatibility.cpp
@@ -9,96 +9,152 @@
#include
#include
-Compatibility::Compatibility() : QObject(nullptr) {}
+Compatibility::Compatibility()
+ : QObject(nullptr)
+{}
-void Compatibility::setOutputCallback(std::function callback) {
+void Compatibility::setOutputCallback(std::function callback)
+{
outputCallback = callback;
}
-void Compatibility::outputProgress(const QString& message) {
- if (outputCallback) {
+void Compatibility::outputProgress(const QString& message)
+{
+ if (outputCallback)
+ {
outputCallback(message);
}
}
-int Compatibility::run(int argc, char* argv[]) {
+int Compatibility::run(int argc, char* argv[])
+{
return runWithDllPaths(QStringList() << "C:\\Windows\\System32\\ntdll.dll");
}
-int Compatibility::runWithDllPaths(const QStringList& dllPaths) {
- qDebug() << QString("Compatibility::runWithDllPaths() called with paths: %1").arg(dllPaths.join(", "));
+int Compatibility::runWithDllPaths(const QStringList& dllPaths)
+{
+ qDebug() << QString("Compatibility::runWithDllPaths() called with paths: %1")
+ .arg(dllPaths.join(", "));
+
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
bool isKernelMode = (syscallMode == "Zw");
- qDebug() << QString("Syscall Mode: %1, Kernel Mode: %2").arg(syscallMode).arg(isKernelMode);
+
+ qDebug() << QString("Syscall Mode: %1, Kernel Mode: %2")
+ .arg(syscallMode)
+ .arg(isKernelMode);
+
QString asmFile = getAsmFilePath(isKernelMode);
qDebug() << QString("ASM File Path: %1").arg(asmFile);
+
QStringList dllPathsToUse = dllPaths;
- if (dllPathsToUse.isEmpty()) {
+
+ if (dllPathsToUse.isEmpty())
+ {
dllPathsToUse << "C:\\Windows\\System32\\ntdll.dll";
}
+
qDebug() << QString("Using DLL Paths: %1").arg(dllPathsToUse.join(", "));
+
validateSyscalls(asmFile, dllPathsToUse);
return 0;
}
-QList Compatibility::readSyscalls(const QString& asmFile) {
+QList Compatibility::readSyscalls(const QString& asmFile)
+{
QList syscalls;
QMap uniqueOffsets;
QMap uniqueNames;
+
QFile file(asmFile);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
qWarning() << "Failed to open ASM File:" << asmFile;
return syscalls;
}
+
QTextStream stream(&file);
SyscallInfo currentSyscall;
bool hasCurrentSyscall = false;
+
qDebug() << "Debug: Reading ASM File:" << asmFile;
int lineCount = 0;
- while (!stream.atEnd()) {
+ while (!stream.atEnd())
+ {
QString line = stream.readLine();
lineCount++;
- if (lineCount <= 10) {
+
+ if (lineCount <= 10)
+ {
qDebug() << "Debug: Line" << lineCount << ":" << line.trimmed();
}
+
QRegularExpression procMatch("((Sys|SysK|SysInline)\\w+)\\s+PROC");
QRegularExpressionMatch match = procMatch.match(line.trimmed());
- if (line.trimmed().contains("PROC") && (line.trimmed().startsWith("Sys") || line.trimmed().startsWith("SysK") || line.trimmed().startsWith("SysInline"))) {
+
+ if (line.trimmed().contains("PROC") &&
+ (line.trimmed().startsWith("Sys") ||
+ line.trimmed().startsWith("SysK") ||
+ line.trimmed().startsWith("SysInline")))
+ {
qDebug() << "Debug: Line contains PROC and starts with Sys/SysK/SysInline:" << line.trimmed();
- if (!match.hasMatch()) {
+
+ if (!match.hasMatch())
+ {
qDebug() << "Debug: But Regex didn't match!";
}
}
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
qDebug() << "Debug: Found Syscall Line:" << line.trimmed() << "Captured:" << match.captured(1);
}
- if (match.hasMatch()) {
- if (hasCurrentSyscall && !syscalls.contains(currentSyscall)) {
+ if (match.hasMatch())
+ {
+ if (hasCurrentSyscall && !syscalls.contains(currentSyscall))
+ {
syscalls.append(currentSyscall);
}
+
QString syscallName = match.captured(1);
QString baseName;
int version = 1;
+
QRegularExpression versionMatch(R"((Sys|SysK|SysInline)(\w+?)([A-Z])?$)");
QRegularExpressionMatch vMatch = versionMatch.match(syscallName);
- if (vMatch.hasMatch()) {
- QString prefix = vMatch.captured(1); // "Sys", "SysK", or "SysInline"
- QString namePart = vMatch.captured(2); // the actual function name
+
+ if (vMatch.hasMatch())
+ {
+ QString prefix = vMatch.captured(1); // "Sys", "SysK", or "SysInline"
+ QString namePart = vMatch.captured(2); // the actual function name
QString versionPart = vMatch.captured(3); // the version letter
+
baseName = prefix + namePart;
- if (!versionPart.isEmpty()) {
+
+ if (!versionPart.isEmpty())
+ {
// convert letter to version number A=2, B=3, C=4, etc
version = versionPart.at(0).toLatin1() - 'A' + 2;
- } else {
+ }
+ else
+ {
version = 1;
}
- } else {
+ }
+ else
+ {
baseName = syscallName;
version = 1;
}
- qDebug() << QString("Debug: Parsed Syscall '%1' -> BaseName='%2', Version=%3").arg(syscallName).arg(baseName).arg(version);
- currentSyscall = SyscallInfo{
+
+ qDebug() << QString("Debug: Parsed Syscall '%1' -> BaseName='%2', Version=%3")
+ .arg(syscallName)
+ .arg(baseName)
+ .arg(version);
+
+ currentSyscall = SyscallInfo
+ {
syscallName,
baseName,
version,
@@ -108,36 +164,51 @@ QList Compatibility::readSyscalls(const QString& asm
false,
""
};
+
hasCurrentSyscall = true;
QString nameKey = QString("%1_%2").arg(baseName).arg(version);
- if (uniqueNames.contains(nameKey)) {
+
+ if (uniqueNames.contains(nameKey))
+ {
currentSyscall.duplicateName = true;
currentSyscall.duplicateNameWith = uniqueNames[nameKey];
- } else {
+ }
+ else
+ {
currentSyscall.duplicateName = false;
uniqueNames[nameKey] = syscallName;
}
}
QRegularExpression offsetMatch(R"(mov\s+(eax|rax),\s*(0x[0-9A-Fa-f]+|[0-9A-Fa-f]+)h?)");
QRegularExpressionMatch oMatch = offsetMatch.match(line);
- if (hasCurrentSyscall && oMatch.hasMatch() && !line.trimmed().startsWith(";")) {
+
+ if (hasCurrentSyscall && oMatch.hasMatch() && !line.trimmed().startsWith(";"))
+ {
QString offsetValue = oMatch.captured(2);
bool ok;
int offset;
- if (offsetValue.startsWith("0x")) {
+
+ if (offsetValue.startsWith("0x"))
+ {
offset = offsetValue.toInt(&ok, 16);
- } else {
+ }
+ else
+ {
offset = offsetValue.remove("h").toInt(&ok, 16);
}
-
- if (ok) {
+
+ if (ok)
+ {
currentSyscall.offset = offset;
QString offsetKey = QString("%1_%2").arg(offset).arg(currentSyscall.version);
-
- if (uniqueOffsets.contains(offsetKey)) {
+
+ if (uniqueOffsets.contains(offsetKey))
+ {
currentSyscall.duplicateOffset = true;
currentSyscall.duplicateOffsetWith = uniqueOffsets[offsetKey];
- } else {
+ }
+ else
+ {
currentSyscall.duplicateOffset = false;
uniqueOffsets[offsetKey] = currentSyscall.name;
}
@@ -145,37 +216,51 @@ QList Compatibility::readSyscalls(const QString& asm
}
QRegularExpression dbMatch(R"(db\s+04Ch,\s*08Bh,\s*0D1h,\s*0B8h,\s*0([0-9A-Fa-f]+)h,\s*0([0-9A-Fa-f]+)h)");
QRegularExpressionMatch dbOMatch = dbMatch.match(line);
- if (hasCurrentSyscall && dbOMatch.hasMatch() && !line.trimmed().startsWith(";")) {
+
+ if (hasCurrentSyscall && dbOMatch.hasMatch() && !line.trimmed().startsWith(";"))
+ {
QString lowByte = dbOMatch.captured(1);
QString highByte = dbOMatch.captured(2);
bool ok1, ok2;
int low = lowByte.toInt(&ok1, 16);
int high = highByte.toInt(&ok2, 16);
- if (ok1 && ok2) {
+
+ if (ok1 && ok2)
+ {
int offset = low | (high << 8);
currentSyscall.offset = offset;
QString offsetKey = QString("%1_%2").arg(offset).arg(currentSyscall.version);
- if (uniqueOffsets.contains(offsetKey)) {
+
+ if (uniqueOffsets.contains(offsetKey))
+ {
currentSyscall.duplicateOffset = true;
currentSyscall.duplicateOffsetWith = uniqueOffsets[offsetKey];
- } else {
+ }
+ else
+ {
currentSyscall.duplicateOffset = false;
uniqueOffsets[offsetKey] = currentSyscall.name;
}
}
}
- if (hasCurrentSyscall && line.contains("ENDP") && !syscalls.contains(currentSyscall)) {
+
+ if (hasCurrentSyscall && line.contains("ENDP") && !syscalls.contains(currentSyscall))
+ {
syscalls.append(currentSyscall);
}
}
- if (hasCurrentSyscall && !syscalls.contains(currentSyscall)) {
+
+ if (hasCurrentSyscall && !syscalls.contains(currentSyscall))
+ {
syscalls.append(currentSyscall);
}
+
file.close();
return syscalls;
}
-void Compatibility::printLegend() {
+void Compatibility::printLegend()
+{
outputProgress("");
outputProgress(Colors::BOLD() + "Bind Legend:" + Colors::ENDC());
outputProgress(Colors::BOLD() + "Nt/Zw = Indicates type of syscall stub found" + Colors::ENDC());
@@ -190,129 +275,198 @@ void Compatibility::printLegend() {
outputProgress("");
}
-void Compatibility::validateSyscalls(const QString& asmFile, const QStringList& dllPaths) {
+void Compatibility::validateSyscalls(const QString& asmFile, const QStringList& dllPaths)
+{
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
bool isZwMode = (syscallMode == "Zw");
QString modeDisplay = isZwMode ? "Zw" : "Nt";
+
QList syscalls = readSyscalls(asmFile);
- outputProgress(Colors::BOLD() + QString("Found %1 Syscalls in syscaller.asm").arg(syscalls.size()) + Colors::ENDC());
- for (int i = 0; i < qMin(3, syscalls.size()); ++i) {
- qDebug() << QString("Debug: Found Syscall %1 with Offset %2").arg(syscalls[i].name).arg(syscalls[i].offset);
+ outputProgress(Colors::BOLD() + QString("Found %1 Syscalls in syscaller.asm")
+ .arg(syscalls.size()) + Colors::ENDC());
+
+ for (int i = 0; i < qMin(3, syscalls.size()); ++i)
+ {
+ qDebug() << QString("Debug: Found Syscall %1 with Offset %2")
+ .arg(syscalls[i].name)
+ .arg(syscalls[i].offset);
}
+
QMap> syscallTables;
QStringList dllPathsToUse = dllPaths;
- if (dllPathsToUse.isEmpty()) {
+
+ if (dllPathsToUse.isEmpty())
+ {
dllPathsToUse << "C:\\Windows\\System32\\ntdll.dll";
}
+
QString mainDllPath = dllPathsToUse.first();
- outputProgress(Colors::OKBLUE() + QString("Processing Primary NTDLL: %1").arg(mainDllPath) + Colors::ENDC());
+ outputProgress(Colors::OKBLUE() + QString("Processing Primary NTDLL: %1")
+ .arg(mainDllPath) + Colors::ENDC());
+
syscallTables[0] = SyscallExtractor::getSyscallsFromDll(mainDllPath);
qDebug() << QString("Found %1 Syscalls in Primary NTDLL").arg(syscallTables[0].size());
- for (int i = 1; i < dllPathsToUse.size(); ++i) {
+
+ for (int i = 1; i < dllPathsToUse.size(); ++i)
+ {
QString additionalDllPath = dllPathsToUse[i];
- if (!additionalDllPath.isEmpty() && QFile::exists(additionalDllPath)) {
- outputProgress(Colors::OKBLUE() + QString("Processing Additional NTDLL %1: %2").arg(i).arg(additionalDllPath) + Colors::ENDC());
+
+ if (!additionalDllPath.isEmpty() && QFile::exists(additionalDllPath))
+ {
+ outputProgress(Colors::OKBLUE() + QString("Processing Additional NTDLL %1: %2")
+ .arg(i).arg(additionalDllPath) + Colors::ENDC());
+
syscallTables[i] = SyscallExtractor::getSyscallsFromDll(additionalDllPath);
- qDebug() << QString("Found %1 Syscalls in Additional NTDLL %2").arg(syscallTables[i].size()).arg(i);
- } else {
+ qDebug() << QString("Found %1 Syscalls in Additional NTDLL %2")
+ .arg(syscallTables[i].size()).arg(i);
+ }
+ else
+ {
qWarning() << "Additional DLL path does not exist or is empty:" << additionalDllPath;
}
}
+
printLegend();
int valid = 0, invalid = 0, duplicates = 0;
- for (const SyscallInfo& syscall : syscalls) {
+ for (const SyscallInfo& syscall : syscalls)
+ {
int version = syscall.version;
int dllIndex = (version == 1) ? 0 : (version - 1); // version 1 = table 0, version 2 = table 1, etc.
- qDebug() << QString("Debug: Checking Syscall '%1' (version %2) against Table %3").arg(syscall.name).arg(version).arg(dllIndex);
- if (!syscallTables.contains(dllIndex)) {
- outputProgress(Colors::WARNING() + QString("Warning: No Syscall Table found for version %1 (Table %2)").arg(version).arg(dllIndex) + Colors::ENDC());
+
+ qDebug() << QString("Debug: Checking Syscall '%1' (version %2) against Table %3")
+ .arg(syscall.name).arg(version).arg(dllIndex);
+
+ if (!syscallTables.contains(dllIndex))
+ {
+ outputProgress(Colors::WARNING() + QString("Warning: No Syscall Table found for version %1 (Table %2)")
+ .arg(version).arg(dllIndex) + Colors::ENDC());
continue;
}
+
QMap syscallNumbers = syscallTables[dllIndex];
+
// remove version suffix for DLL lookup
QString baseName = syscall.baseName;
QString expectedName;
- if (baseName.startsWith("SysK")) {
+
+ if (baseName.startsWith("SysK"))
+ {
expectedName = "Nt" + baseName.mid(4);
- } else if (baseName.startsWith("SysInline")) {
+ }
+ else if (baseName.startsWith("SysInline"))
+ {
expectedName = "Nt" + baseName.mid(9);
- } else if (baseName.startsWith("Sys")) {
+ }
+ else if (baseName.startsWith("Sys"))
+ {
expectedName = "Nt" + baseName.mid(3);
- } else {
+ }
+ else
+ {
expectedName = baseName;
}
- if (expectedName.endsWith("A") || expectedName.endsWith("B") || expectedName.endsWith("C") ||
- expectedName.endsWith("D") || expectedName.endsWith("E") || expectedName.endsWith("F")) {
+
+ if (expectedName.endsWith("A") || expectedName.endsWith("B") || expectedName.endsWith("C") ||
+ expectedName.endsWith("D") || expectedName.endsWith("E") || expectedName.endsWith("F"))
+ {
expectedName = expectedName.left(expectedName.length() - 1);
}
+
int actualOffset = syscallNumbers.value(expectedName, 0);
// check for duplicates only within same table
bool isDuplicate = false;
QString dupType, dupWith;
- if (syscall.duplicateOffset || syscall.duplicateName) {
- if (syscall.duplicateOffset && syscall.duplicateName) {
+
+ if (syscall.duplicateOffset || syscall.duplicateName)
+ {
+ if (syscall.duplicateOffset && syscall.duplicateName)
+ {
dupType = "Duplicate Offset & Name";
- if (syscall.duplicateOffsetWith == syscall.duplicateNameWith) {
+
+ if (syscall.duplicateOffsetWith == syscall.duplicateNameWith)
+ {
dupWith = QString("Offset & Name with %1").arg(syscall.duplicateOffsetWith);
- } else {
- dupWith = QString("Offset with %1 | Name with %2").arg(syscall.duplicateOffsetWith).arg(syscall.duplicateNameWith);
}
- } else if (syscall.duplicateOffset) {
+ else
+ {
+ dupWith = QString("Offset with %1 | Name with %2")
+ .arg(syscall.duplicateOffsetWith)
+ .arg(syscall.duplicateNameWith);
+ }
+ }
+ else if (syscall.duplicateOffset)
+ {
dupType = "Duplicate Offset";
dupWith = QString("with %1").arg(syscall.duplicateOffsetWith);
- } else {
+ }
+ else
+ {
dupType = "Duplicate Name";
dupWith = QString("with %1").arg(syscall.duplicateNameWith);
}
+
isDuplicate = true;
}
- if (isDuplicate) {
+ if (isDuplicate)
+ {
duplicates++;
QString prefix = (syscall.offset == actualOffset) ? "v" : "i";
- outputProgress(Colors::WARNING() + QString("%1: %2 (%3) %40x%5 f0x%6 (DUP) %7").arg(syscall.name)
- .arg(dupType)
- .arg(modeDisplay)
- .arg(prefix)
- .arg(syscall.offset, 0, 16)
- .arg(actualOffset, 0, 16)
- .arg(dupWith) + Colors::ENDC());
+
+ outputProgress(Colors::WARNING() + QString("%1: %2 (%3) %40x%5 f0x%6 (DUP) %7")
+ .arg(syscall.name)
+ .arg(dupType)
+ .arg(modeDisplay)
+ .arg(prefix)
+ .arg(syscall.offset, 0, 16)
+ .arg(actualOffset, 0, 16)
+ .arg(dupWith) + Colors::ENDC());
continue;
}
- if (syscallNumbers.contains(expectedName)) {
- if (syscall.offset == syscallNumbers[expectedName]) {
+
+ if (syscallNumbers.contains(expectedName))
+ {
+ if (syscall.offset == syscallNumbers[expectedName])
+ {
valid++;
outputProgress(Colors::OKGREEN() + QString("%1: Found (%2) v0x%3 f0x%4 (MATCH)")
- .arg(syscall.name)
- .arg(modeDisplay)
- .arg(syscall.offset, 0, 16)
- .arg(syscallNumbers[expectedName], 0, 16) + Colors::ENDC());
- } else {
+ .arg(syscall.name)
+ .arg(modeDisplay)
+ .arg(syscall.offset, 0, 16)
+ .arg(syscallNumbers[expectedName], 0, 16) + Colors::ENDC());
+ }
+ else
+ {
invalid++;
outputProgress(Colors::FAIL() + QString("%1: Found (%2) i0x%3 f0x%4 (MISMATCH)")
- .arg(syscall.name)
- .arg(modeDisplay)
- .arg(syscall.offset, 0, 16)
- .arg(syscallNumbers[expectedName], 0, 16) + Colors::ENDC());
+ .arg(syscall.name)
+ .arg(modeDisplay)
+ .arg(syscall.offset, 0, 16)
+ .arg(syscallNumbers[expectedName], 0, 16) + Colors::ENDC());
}
- } else {
+ }
+ else
+ {
invalid++;
outputProgress(Colors::FAIL() + QString("%1: Not Found (%2) i0x%3 f0x%4 (MISMATCH)")
- .arg(syscall.name)
- .arg(modeDisplay)
- .arg(syscall.offset, 0, 16)
- .arg(actualOffset, 0, 16) + Colors::ENDC());
+ .arg(syscall.name)
+ .arg(modeDisplay)
+ .arg(syscall.offset, 0, 16)
+ .arg(actualOffset, 0, 16) + Colors::ENDC());
}
}
- outputProgress(Colors::BOLD() + QString("Valid: ") + Colors::OKGREEN() + QString::number(valid) + Colors::ENDC() +
- Colors::BOLD() + QString(", Invalid: ") + Colors::FAIL() + QString::number(invalid) + Colors::ENDC() +
+
+ outputProgress(Colors::BOLD() + QString("Valid: ") + Colors::OKGREEN() + QString::number(valid) + Colors::ENDC() +
+ Colors::BOLD() + QString(", Invalid: ") + Colors::FAIL() + QString::number(invalid) + Colors::ENDC() +
Colors::BOLD() + QString(", Duplicates: ") + Colors::WARNING() + QString::number(duplicates) + Colors::ENDC());
}
-QString Compatibility::getIniPath() {
+QString Compatibility::getIniPath()
+{
return PathUtils::getIniPath();
}
-QString Compatibility::getAsmFilePath(bool isKernelMode) {
+QString Compatibility::getAsmFilePath(bool isKernelMode)
+{
return PathUtils::getSysCallerAsmPath(isKernelMode);
}
diff --git a/Bind/src/Core/Integrity/Validator/Validator.cpp b/Bind/src/Core/Integrity/Validator/Validator.cpp
index 91783d4..7612291 100644
--- a/Bind/src/Core/Integrity/Validator/Validator.cpp
+++ b/Bind/src/Core/Integrity/Validator/Validator.cpp
@@ -10,303 +10,465 @@
#include
#include
-Validator::Validator() : QObject(nullptr) {}
+Validator::Validator()
+ : QObject(nullptr)
+{}
-void Validator::setOutputCallback(std::function callback) {
+void Validator::setOutputCallback(std::function callback)
+{
outputCallback = callback;
}
-void Validator::outputProgress(const QString& message) {
- if (outputCallback) {
+void Validator::outputProgress(const QString& message)
+{
+ if (outputCallback)
+ {
outputCallback(message);
}
}
-int Validator::run(int argc, char* argv[]) {
+int Validator::run(int argc, char* argv[])
+{
return runWithDllPaths(QStringList() << "C:\\Windows\\System32\\ntdll.dll");
}
-int Validator::runWithDllPaths(const QStringList& dllPaths) {
- qDebug() << QString("Validator::runWithDllPaths() called with paths: %1").arg(dllPaths.join(", "));
+int Validator::runWithDllPaths(const QStringList& dllPaths)
+{
+ qDebug() << QString("Validator::runWithDllPaths() called with paths: %1")
+ .arg(dllPaths.join(", "));
+
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
bool isKernelMode = (syscallMode == "Zw");
- qDebug() << QString("Syscall Mode: %1, Kernel Mode: %2").arg(syscallMode).arg(isKernelMode);
+
+ qDebug() << QString("Syscall Mode: %1, Kernel Mode: %2")
+ .arg(syscallMode)
+ .arg(isKernelMode);
+
QString baseDir = PathUtils::getProjectRoot();
QString asmFile = getAsmFilePath(isKernelMode);
+
qDebug() << QString("Base Dir: %1").arg(baseDir);
qDebug() << QString("ASM File Path: %1").arg(asmFile);
+
QStringList dllPathsToUse = dllPaths;
- if (dllPathsToUse.isEmpty()) {
+
+ if (dllPathsToUse.isEmpty())
+ {
dllPathsToUse << "C:\\Windows\\System32\\ntdll.dll";
}
+
qDebug() << QString("Using DLL Paths: %1").arg(dllPathsToUse.join(", "));
+
QMap> syscallTables;
QString mainDllPath = dllPathsToUse.first();
- outputProgress(Colors::OKBLUE() + QString("Processing Primary NTDLL: %1").arg(mainDllPath) + Colors::ENDC());
+
+ outputProgress(Colors::OKBLUE() + QString("Processing Primary NTDLL: %1")
+ .arg(mainDllPath) + Colors::ENDC());
+
qDebug() << QString("Processing Primary NTDLL: %1").arg(mainDllPath);
qDebug() << "DLL Path Exists:" << QFile::exists(mainDllPath);
- if (!QFile::exists(mainDllPath)) {
+
+ if (!QFile::exists(mainDllPath))
+ {
qWarning() << "Primary DLL path does not exist:" << mainDllPath;
qWarning() << "Using default path: C:\\Windows\\System32\\ntdll.dll";
mainDllPath = "C:\\Windows\\System32\\ntdll.dll";
- if (!QFile::exists(mainDllPath)) {
+
+ if (!QFile::exists(mainDllPath))
+ {
qCritical() << "Default DLL path also does not exist! Cannot proceed.";
return -1;
}
}
+
syscallTables[0] = SyscallExtractor::getSyscallsFromDll(mainDllPath);
qDebug() << QString("Found %1 Syscalls in Primary NTDLL").arg(syscallTables[0].size());
- if (syscallTables[0].size() > 0) {
+
+ if (syscallTables[0].size() > 0)
+ {
qDebug() << "Sample Syscalls from Primary NTDLL:";
int count = 0;
- for (auto it = syscallTables[0].begin(); it != syscallTables[0].end() && count < 5; ++it, ++count) {
+
+ for (auto it = syscallTables[0].begin(); it != syscallTables[0].end() && count < 5; ++it, ++count)
+ {
qDebug() << " " << it.key() << "->" << it.value();
}
}
- for (int i = 1; i < dllPathsToUse.size(); ++i) {
+
+ for (int i = 1; i < dllPathsToUse.size(); ++i)
+ {
QString additionalDllPath = dllPathsToUse[i];
- if (!additionalDllPath.isEmpty() && QFile::exists(additionalDllPath)) {
- outputProgress(Colors::OKBLUE() + QString("Processing Additional NTDLL %1: %2").arg(i).arg(additionalDllPath) + Colors::ENDC());
+
+ if (!additionalDllPath.isEmpty() && QFile::exists(additionalDllPath))
+ {
+ outputProgress(Colors::OKBLUE() + QString("Processing Additional NTDLL %1: %2")
+ .arg(i).arg(additionalDllPath) + Colors::ENDC());
+
qDebug() << "Processing Additional NTDLL" << i << ":" << additionalDllPath;
syscallTables[i] = SyscallExtractor::getSyscallsFromDll(additionalDllPath);
- } else {
+ }
+ else
+ {
qWarning() << "Additional DLL path does not exist or is empty:" << additionalDllPath;
}
}
+
qDebug() << QString("About to call updateSyscalls...");
updateSyscalls(asmFile, syscallTables);
qDebug() << QString("updateSyscalls completed");
+
bool bindingsEnabled = settings.value("general/bindings_enabled", false).toBool();
bool indirectAssemblyMode = settings.value("general/indirect_assembly", false).toBool();
- if (bindingsEnabled && !isKernelMode) {
+
+ if (bindingsEnabled && !isKernelMode)
+ {
qDebug() << QString("Bindings enabled, parsing updated ASM file for Sys* PROC patterns...");
+
QStringList syscallNames;
QFile file(asmFile);
- if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
QTextStream stream(&file);
QRegularExpression procRegex(R"(^\s*(Sys\w+)\s+PROC)");
- while (!stream.atEnd()) {
+
+ while (!stream.atEnd())
+ {
QString line = stream.readLine();
QRegularExpressionMatch match = procRegex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString syscallName = match.captured(1);
syscallNames.append(syscallName);
qDebug() << "Found Syscall for DEF File:" << syscallName;
}
}
+
file.close();
}
- if (indirectAssemblyMode && syscallMode == "Nt") {
+
+ if (indirectAssemblyMode && syscallMode == "Nt")
+ {
syscallNames.append("GetSyscallNumber");
syscallNames.append("InitializeResolver");
syscallNames.append("CleanupResolver");
qDebug() << "Added resolver functions to DEF file";
}
+
qDebug() << QString("Found %1 Syscalls for DEF File").arg(syscallNames.size());
QString defPath = getDefFilePath();
updateDefFile(syscallNames, defPath);
qDebug() << QString("Updated DEF File: %1").arg(defPath);
}
+
return 0;
}
-QMap Validator::parseAsmFile(const QString& asmFile) {
+QMap Validator::parseAsmFile(const QString& asmFile)
+{
QMap syscalls;
QFile file(asmFile);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
return syscalls;
}
+
QTextStream stream(&file);
QStringList lines;
- while (!stream.atEnd()) {
+
+ while (!stream.atEnd())
+ {
lines.append(stream.readLine());
}
+
file.close();
+
QString currentSyscall;
int startIndex = -1;
- for (int i = 0; i < lines.size(); ++i) {
+
+ for (int i = 0; i < lines.size(); ++i)
+ {
QString line = lines[i];
QRegularExpression procMatch(R"((Sys\w+|SC\w+)\s+PROC)");
QRegularExpressionMatch match = procMatch.match(line);
- if (match.hasMatch()) {
- if (!currentSyscall.isEmpty()) {
+
+ if (match.hasMatch())
+ {
+ if (!currentSyscall.isEmpty())
+ {
syscalls[currentSyscall].end = i - 1;
}
+
currentSyscall = match.captured(1);
syscalls[currentSyscall].start = i;
syscalls[currentSyscall].end = -1;
- } else if (!currentSyscall.isEmpty() && line.contains("ENDP")) {
+ }
+ else if (!currentSyscall.isEmpty() && line.contains("ENDP"))
+ {
syscalls[currentSyscall].end = i;
currentSyscall.clear();
}
}
- if (!currentSyscall.isEmpty()) {
+
+ if (!currentSyscall.isEmpty())
+ {
syscalls[currentSyscall].end = lines.size() - 1;
}
- for (auto it = syscalls.begin(); it != syscalls.end(); ++it) {
+
+ for (auto it = syscalls.begin(); it != syscalls.end(); ++it)
+ {
int start = it.value().start;
int end = it.value().end;
- if (start >= 0 && end >= start && end < lines.size()) {
- for (int i = start; i <= end; ++i) {
+
+ if (start >= 0 && end >= start && end < lines.size())
+ {
+ for (int i = start; i <= end; ++i)
+ {
it.value().content.append(lines[i]);
}
}
}
+
return syscalls;
}
-void Validator::updateSyscalls(const QString& asmFile, const QMap>& syscallTables) {
+void Validator::updateSyscalls(const QString& asmFile, const QMap>& syscallTables)
+{
QFile file(asmFile);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
qDebug() << "Failed to open ASM File for reading:" << asmFile;
return;
}
+
QStringList lines;
QTextStream stream(&file);
- while (!stream.atEnd()) {
+
+ while (!stream.atEnd())
+ {
lines.append(stream.readLine());
}
+
file.close();
+
int numTables = syscallTables.size();
- if (numTables == 0) {
+
+ if (numTables == 0)
+ {
qDebug() << "No Syscall Tables provided. Aborting.";
return;
}
- outputProgress(Colors::OKBLUE() + QString("Processing %1 Syscall Table(s)...").arg(numTables) + Colors::ENDC());
+
+ outputProgress(Colors::OKBLUE() + QString("Processing %1 Syscall Table(s)...")
+ .arg(numTables) + Colors::ENDC());
+
QSettings settings(getIniPath(), QSettings::IniFormat);
QStringList selectedSyscalls = settings.value("integrity/selected_syscalls", QStringList()).toStringList();
+
qDebug() << "Selected Syscalls from Settings:" << selectedSyscalls;
bool useAllSyscalls = selectedSyscalls.isEmpty();
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK";
bool inlineAssemblyMode = settings.value("general/inline_assembly", false).toBool();
bool indirectAssemblyMode = settings.value("general/indirect_assembly", false).toBool();
- if (inlineAssemblyMode && syscallMode == "Nt") {
+
+ if (inlineAssemblyMode && syscallMode == "Nt")
+ {
syscallPrefix = "SysInline";
outputProgress(Colors::OKBLUE() + "Using SysInline prefix" + Colors::ENDC());
- } else if (indirectAssemblyMode && syscallMode == "Nt") {
+ }
+ else if (indirectAssemblyMode && syscallMode == "Nt")
+ {
syscallPrefix = "SysIndirect";
outputProgress(Colors::OKBLUE() + "Using SysIndirect prefix" + Colors::ENDC());
}
QMap scStubs;
QString currentStub;
int startIndex = -1;
- for (int i = 0; i < lines.size(); ++i) {
+
+ for (int i = 0; i < lines.size(); ++i)
+ {
QString line = lines[i];
QRegularExpression procMatch(R"(SC(\w+)\s+PROC)");
QRegularExpressionMatch match = procMatch.match(line);
- if (match.hasMatch()) {
- if (!currentStub.isEmpty()) {
+
+ if (match.hasMatch())
+ {
+ if (!currentStub.isEmpty())
+ {
scStubs[currentStub].end = i - 1;
}
+
QString baseName = match.captured(1);
currentStub = "SC" + baseName;
scStubs[currentStub].start = i;
scStubs[currentStub].end = -1;
- } else if (!currentStub.isEmpty() && line.contains("ENDP")) {
+ }
+ else if (!currentStub.isEmpty() && line.contains("ENDP"))
+ {
scStubs[currentStub].end = i;
currentStub.clear();
}
}
- if (!currentStub.isEmpty()) {
+
+ if (!currentStub.isEmpty())
+ {
scStubs[currentStub].end = lines.size() - 1;
}
+
// extract content for each SC stub
- for (auto it = scStubs.begin(); it != scStubs.end(); ++it) {
+ for (auto it = scStubs.begin(); it != scStubs.end(); ++it)
+ {
int start = it.value().start;
int end = it.value().end;
- if (start >= 0 && end >= start && end < lines.size()) {
- for (int i = start; i <= end; ++i) {
+
+ if (start >= 0 && end >= start && end < lines.size())
+ {
+ for (int i = start; i <= end; ++i)
+ {
it.value().content.append(lines[i]);
}
}
}
+
qDebug() << "Found" << scStubs.size() << "SC Stubs in ASM File";
- if (scStubs.size() > 0) {
+
+ if (scStubs.size() > 0)
+ {
qDebug() << "SC Stubs Found:";
int count = 0;
- for (auto it = scStubs.begin(); it != scStubs.end() && count < 3; ++it, ++count) {
+
+ for (auto it = scStubs.begin(); it != scStubs.end() && count < 3; ++it, ++count)
+ {
qDebug() << " " << it.key() << "(" << it.value().content.size() << " lines)";
}
}
- if (inlineAssemblyMode && syscallMode == "Zw") {
+ if (inlineAssemblyMode && syscallMode == "Zw")
+ {
outputProgress(Colors::WARNING() + "Inline assembly mode is not supported in kernel mode, disabling." + Colors::ENDC());
inlineAssemblyMode = false;
}
+
QStringList newLines;
int skipUntil = -1;
- for (int i = 0; i < lines.size(); ++i) {
- if (i <= skipUntil) {
+
+ for (int i = 0; i < lines.size(); ++i)
+ {
+ if (i <= skipUntil)
+ {
continue;
}
+
QString line = lines[i];
QRegularExpression procMatch(R"(SC(\w+)\s+PROC)");
QRegularExpressionMatch match = procMatch.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString originalName = match.captured(0);
QString baseName = match.captured(1);
QString originalFuncName = "SC" + baseName;
QString syscallName = syscallPrefix + baseName;
QString checkName = syscallName;
- if (inlineAssemblyMode && syscallName.startsWith("SysInline")) {
- // convert back to Sys prefix for checking against selectedSyscalls
+
+ if (inlineAssemblyMode && syscallName.startsWith("SysInline"))
+ {
+ // convert back to Sys prefix for checking against selectedSyscalls
checkName = "Sys" + syscallName.mid(9);
- } else if (indirectAssemblyMode && syscallName.startsWith("SysIndirect")) {
- // convert back to Sys prefix for checking against selectedSyscalls
+ }
+ else if (indirectAssemblyMode && syscallName.startsWith("SysIndirect"))
+ {
+ // convert back to Sys prefix for checking against selectedSyscalls
checkName = "Sys" + syscallName.mid(11);
}
- if (!useAllSyscalls && !selectedSyscalls.contains(checkName)) {
+
+ if (!useAllSyscalls && !selectedSyscalls.contains(checkName))
+ {
outputProgress(Colors::WARNING() + QString("Skipping %1 (not selected in Settings)").arg(syscallName) + Colors::ENDC());
- if (scStubs.contains(originalFuncName)) {
+
+ if (scStubs.contains(originalFuncName))
+ {
skipUntil = scStubs[originalFuncName].end;
}
+
continue;
}
+
bool foundInAny = false;
- for (auto tableIt = syscallTables.begin(); tableIt != syscallTables.end(); ++tableIt) {
+
+ for (auto tableIt = syscallTables.begin(); tableIt != syscallTables.end(); ++tableIt)
+ {
int tableIdx = tableIt.key();
const QMap& syscallNumbers = tableIt.value();
QString expectedDllName, expectedAltName;
- if (syscallMode == "Nt") {
+
+ if (syscallMode == "Nt")
+ {
expectedDllName = "Nt" + baseName;
expectedAltName = "Zw" + baseName;
- } else {
+ }
+ else
+ {
expectedDllName = "Zw" + baseName;
expectedAltName = "Nt" + baseName;
}
+
int syscallId = -1;
- if (syscallNumbers.contains(expectedDllName)) {
+
+ if (syscallNumbers.contains(expectedDllName))
+ {
syscallId = syscallNumbers[expectedDllName];
- } else if (syscallNumbers.contains(expectedAltName)) {
+ }
+ else if (syscallNumbers.contains(expectedAltName))
+ {
syscallId = syscallNumbers[expectedAltName];
}
- if (syscallId != -1) {
+
+ if (syscallId != -1)
+ {
foundInAny = true;
QString versionSuffix = (tableIdx == 0) ? "" : QString(QChar('A' + tableIdx - 1));
QString versionedSyscallName = syscallPrefix + baseName + versionSuffix;
- if (inlineAssemblyMode) {
+
+ if (inlineAssemblyMode)
+ {
QString inlineStub = InlineAssemblyConverter::convertStubToInline(versionedSyscallName, syscallId);
newLines.append(inlineStub);
newLines.append("");
- } else if (indirectAssemblyMode) {
+ }
+ else if (indirectAssemblyMode)
+ {
QString indirectStub = generateIndirectStub(versionedSyscallName, syscallId);
newLines.append(indirectStub);
newLines.append("");
- } else {
+ }
+ else
+ {
QString procLine = QString("%1 PROC").arg(versionedSyscallName);
newLines.append(procLine);
- if (scStubs.contains(originalFuncName)) {
+
+ if (scStubs.contains(originalFuncName))
+ {
QStringList content = scStubs[originalFuncName].content;
- for (int j = 1; j < content.size() - 1; ++j) {
+
+ for (int j = 1; j < content.size() - 1; ++j)
+ {
QString contentLine = content[j];
- if (contentLine.contains("")) {
+
+ if (contentLine.contains(""))
+ {
contentLine = contentLine.replace("", QString("0%1").arg(syscallId, 0, 16).toUpper());
}
+
QRegularExpression scRegex(R"(\bSC(\w+)\b)");
contentLine.replace(scRegex, syscallPrefix + "\\1" + versionSuffix);
newLines.append(contentLine);
}
+
QString endpLine = content.last().replace(originalFuncName, versionedSyscallName);
newLines.append(endpLine);
newLines.append("");
@@ -314,429 +476,665 @@ void Validator::updateSyscalls(const QString& asmFile, const QMap>& syscallTables, const QStringList& selectedSyscalls, bool useAllSyscalls) {
+void Validator::updateHeaderFile(const QMap>& syscallTables,
+ const QStringList& selectedSyscalls,
+ bool useAllSyscalls)
+{
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
bool isKernelMode = (syscallMode == "Zw");
QString headerFilePath = getHeaderFilePath(isKernelMode);
-
+
QFile file(headerFilePath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
return;
}
+
QStringList lines;
QTextStream stream(&file);
- while (!stream.atEnd()) {
+
+ while (!stream.atEnd())
+ {
lines.append(stream.readLine());
}
+
file.close();
+
QStringList updatedLines;
bool headerPartEnded = false;
QStringList endingLines;
QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK";
bool inlineAssemblyMode = settings.value("general/inline_assembly", false).toBool();
bool indirectAssemblyMode = settings.value("general/indirect_assembly", false).toBool();
- if (inlineAssemblyMode && syscallMode == "Zw") {
+
+ if (inlineAssemblyMode && syscallMode == "Zw")
+ {
inlineAssemblyMode = false;
}
- if (inlineAssemblyMode) {
+
+ if (inlineAssemblyMode)
+ {
syscallPrefix = "SysInline";
outputProgress(Colors::OKBLUE() + "Using SysInline prefix for header file generation" + Colors::ENDC());
- } else if (indirectAssemblyMode && syscallMode == "Nt") {
+ }
+ else if (indirectAssemblyMode && syscallMode == "Nt")
+ {
syscallPrefix = "SysIndirect";
outputProgress(Colors::OKBLUE() + "Using SysIndirect prefix for header file generation" + Colors::ENDC());
}
- for (int i = lines.size() - 1; i >= 0; --i) {
+ for (int i = lines.size() - 1; i >= 0; --i)
+ {
QString line = lines[i].trimmed();
- if (line == "#endif" || line.startsWith("#endif ")) {
+
+ if (line == "#endif" || line.startsWith("#endif "))
+ {
endingLines.insert(0, lines[i]);
int j = i - 1;
- while (j >= 0 && (lines[j].trimmed().isEmpty() || lines[j].trimmed().startsWith("//"))) {
+
+ while (j >= 0 && (lines[j].trimmed().isEmpty() || lines[j].trimmed().startsWith("//")))
+ {
endingLines.insert(0, lines[j]);
--j;
}
+
break;
}
}
+
QMap functionDeclarations;
QString currentFunction;
QStringList functionContent;
- for (int i = 0; i < lines.size(); ++i) {
+
+ for (int i = 0; i < lines.size(); ++i)
+ {
QString line = lines[i];
bool isEndingLine = false;
- for (const QString& endLine : endingLines) {
- if (line == endLine) {
+
+ for (const QString& endLine : endingLines)
+ {
+ if (line == endLine)
+ {
isEndingLine = true;
break;
}
}
- if (isEndingLine) continue;
- if (!headerPartEnded) {
+
+ if (isEndingLine)
+ {
+ continue;
+ }
+
+ if (!headerPartEnded)
+ {
QRegularExpression funcDeclRegex(R"((?:extern\s+"C"\s+)?(?:NTSTATUS|ULONG|BOOLEAN|VOID)\s+((?:SC|Sys|SysK|SysInline|SysIndirect)\w+)\()");
- if (funcDeclRegex.match(line).hasMatch()) {
+ if (funcDeclRegex.match(line).hasMatch())
+ {
headerPartEnded = true;
}
}
- if (!headerPartEnded) {
- if (line.contains("_WIN64") && line.contains("#ifdef")) {
+
+ if (!headerPartEnded)
+ {
+ if (line.contains("_WIN64") && line.contains("#ifdef"))
+ {
updatedLines.append(line);
updatedLines.append("");
continue;
}
- if (line.trimmed() == "extern \"C\" {") {
+
+ if (line.trimmed() == "extern \"C\" {")
+ {
updatedLines.append(line);
updatedLines.append("");
continue;
}
+
updatedLines.append(line);
continue;
}
QRegularExpression funcDeclRegex(R"((?:extern\s+"C"\s+)?(?:NTSTATUS|ULONG|BOOLEAN|VOID)\s+((?:SC|Sys|SysK|SysInline|SysIndirect)\w+)\()");
QRegularExpressionMatch match = funcDeclRegex.match(line);
- if (match.hasMatch()) {
- if (!currentFunction.isEmpty() && !functionContent.isEmpty()) {
+
+ if (match.hasMatch())
+ {
+ if (!currentFunction.isEmpty() && !functionContent.isEmpty())
+ {
functionDeclarations[currentFunction] = functionContent;
functionContent.clear();
}
+
QString originalName = match.captured(1);
QString syscallName;
- if (originalName.startsWith("SC")) {
+
+ if (originalName.startsWith("SC"))
+ {
QString baseName = originalName.mid(2);
syscallName = syscallPrefix + baseName;
- } else if (originalName.startsWith("SysInline")) {
- if (syscallPrefix == "SysInline") {
+ }
+ else if (originalName.startsWith("SysInline"))
+ {
+ if (syscallPrefix == "SysInline")
+ {
QString baseName = originalName.mid(9);
syscallName = originalName;
- } else {
+ }
+ else
+ {
QString baseName = originalName.mid(9);
syscallName = syscallPrefix + baseName;
}
- } else if (originalName.startsWith("Sys")) {
- if (syscallPrefix == "Sys") {
+ }
+ else if (originalName.startsWith("Sys"))
+ {
+ if (syscallPrefix == "Sys")
+ {
QString baseName = originalName.mid(3);
syscallName = originalName;
- } else {
+ }
+ else
+ {
QString baseName = originalName.mid(3);
syscallName = syscallPrefix + baseName;
}
- } else if (originalName.startsWith("SysK")) {
- if (syscallPrefix == "SysK") {
+ }
+ else if (originalName.startsWith("SysK"))
+ {
+ if (syscallPrefix == "SysK")
+ {
QString baseName = originalName.mid(4);
syscallName = originalName;
- } else {
+ }
+ else
+ {
QString baseName = originalName.mid(4);
syscallName = syscallPrefix + baseName;
}
- } else if (originalName.startsWith("SysIndirect")) {
- if (syscallPrefix == "SysIndirect") {
+ }
+ else if (originalName.startsWith("SysIndirect"))
+ {
+ if (syscallPrefix == "SysIndirect")
+ {
QString baseName = originalName.mid(11);
syscallName = originalName;
- } else {
+ }
+ else
+ {
QString baseName = originalName.mid(11);
syscallName = syscallPrefix + baseName;
}
}
+
QString checkName = syscallName;
- if (inlineAssemblyMode && syscallName.startsWith("SysInline")) {
+
+ if (inlineAssemblyMode && syscallName.startsWith("SysInline"))
+ {
// convert back to Sys prefix for checking against selectedSyscalls
checkName = "Sys" + syscallName.mid(9);
- } else if (indirectAssemblyMode && syscallName.startsWith("SysIndirect")) {
+ }
+ else if (indirectAssemblyMode && syscallName.startsWith("SysIndirect"))
+ {
// convert back to Sys prefix for checking against selectedSyscalls
checkName = "Sys" + syscallName.mid(11);
}
- if (useAllSyscalls || selectedSyscalls.contains(checkName)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(checkName))
+ {
currentFunction = syscallName;
QString modifiedLine = line;
- modifiedLine.replace(QRegularExpression(QString(R"(\b%1\b)").arg(QRegularExpression::escape(originalName))), syscallName);
+ modifiedLine.replace(QRegularExpression(QString(R"(\b%1\b)")
+ .arg(QRegularExpression::escape(originalName))), syscallName);
functionContent.append(modifiedLine);
- } else {
+ }
+ else
+ {
currentFunction.clear();
}
- } else if (!currentFunction.isEmpty()) {
- if (line.contains("SC")) {
+ }
+ else if (!currentFunction.isEmpty())
+ {
+ if (line.contains("SC"))
+ {
QString modifiedLine = line;
QRegularExpression scRegex(R"(\bSC(\w+)\b)");
QRegularExpressionMatchIterator it = scRegex.globalMatch(line);
- while (it.hasNext()) {
+
+ while (it.hasNext())
+ {
QRegularExpressionMatch scMatch = it.next();
QString scName = scMatch.captured(0);
QString baseName = scMatch.captured(1);
QString sysName = syscallPrefix + baseName;
modifiedLine.replace(scName, sysName);
}
+
functionContent.append(modifiedLine);
- } else {
+ }
+ else
+ {
functionContent.append(line);
}
- if (line.trimmed() == ");") {
+
+ if (line.trimmed() == ");")
+ {
functionDeclarations[currentFunction] = functionContent;
functionContent.clear();
currentFunction.clear();
}
}
}
- if (!currentFunction.isEmpty() && !functionContent.isEmpty()) {
+ if (!currentFunction.isEmpty() && !functionContent.isEmpty())
+ {
functionDeclarations[currentFunction] = functionContent;
}
+
qDebug() << "Function declarations found:" << functionDeclarations.size();
- for (auto it = functionDeclarations.begin(); it != functionDeclarations.end(); ++it) {
+
+ for (auto it = functionDeclarations.begin(); it != functionDeclarations.end(); ++it)
+ {
qDebug() << " Function:" << it.key();
}
+
int numTables = syscallTables.size();
- for (auto funcIt = functionDeclarations.begin(); funcIt != functionDeclarations.end(); ++funcIt) {
+
+ for (auto funcIt = functionDeclarations.begin(); funcIt != functionDeclarations.end(); ++funcIt)
+ {
QString funcName = funcIt.key();
QStringList content = funcIt.value();
QString baseName;
- if (funcName.startsWith(syscallPrefix)) {
+
+ if (funcName.startsWith(syscallPrefix))
+ {
baseName = funcName.mid(syscallPrefix.length());
- } else if (funcName.startsWith("SysInline")) {
+ }
+ else if (funcName.startsWith("SysInline"))
+ {
baseName = funcName.mid(9);
- } else if (funcName.startsWith("SysIndirect")) {
+ }
+ else if (funcName.startsWith("SysIndirect"))
+ {
baseName = funcName.mid(11);
- } else {
+ }
+ else
+ {
baseName = funcName;
}
+
bool foundInAnyTable = false;
- for (auto tableIt = syscallTables.begin(); tableIt != syscallTables.end(); ++tableIt) {
+
+ for (auto tableIt = syscallTables.begin(); tableIt != syscallTables.end(); ++tableIt)
+ {
int tableIdx = tableIt.key();
QMap syscallNumbers = tableIt.value();
QString expectedDllName, expectedAltName;
- if (syscallMode == "Nt") {
+
+ if (syscallMode == "Nt")
+ {
expectedDllName = "Nt" + baseName;
expectedAltName = "Zw" + baseName;
- } else {
+ }
+ else
+ {
expectedDllName = "Zw" + baseName;
expectedAltName = "Nt" + baseName;
}
+
int syscallId = -1;
- if (syscallNumbers.contains(expectedDllName)) {
+
+ if (syscallNumbers.contains(expectedDllName))
+ {
syscallId = syscallNumbers[expectedDllName];
- } else if (syscallNumbers.contains(expectedAltName)) {
+ }
+ else if (syscallNumbers.contains(expectedAltName))
+ {
syscallId = syscallNumbers[expectedAltName];
}
- if (syscallId != -1) {
- outputProgress(Colors::OKGREEN() + QString("Found %1 in Table %2 with ID %3").arg(expectedDllName).arg(tableIdx).arg(syscallId) + Colors::ENDC());
+
+ if (syscallId != -1)
+ {
+ outputProgress(Colors::OKGREEN() + QString("Found %1 in Table %2 with ID %3")
+ .arg(expectedDllName).arg(tableIdx).arg(syscallId) + Colors::ENDC());
foundInAnyTable = true;
break;
- } else {
+ }
+ else
+ {
qDebug() << " Not found:" << expectedDllName << "or" << expectedAltName << "in Table" << tableIdx;
}
}
- if (!foundInAnyTable) {
- outputProgress(Colors::FAIL() + QString("Removing %1 from header (not found in any ntdll.dll)").arg(funcName) + Colors::ENDC());
+
+ if (!foundInAnyTable)
+ {
+ outputProgress(Colors::FAIL() + QString("Removing %1 from header (not found in any ntdll.dll)")
+ .arg(funcName) + Colors::ENDC());
continue;
}
// add non versioned functions for table 0
- if (syscallTables.contains(0)) {
+ if (syscallTables.contains(0))
+ {
QMap table0 = syscallTables[0];
QString expectedDllName, expectedAltName;
- if (syscallMode == "Nt") {
+
+ if (syscallMode == "Nt")
+ {
expectedDllName = "Nt" + baseName;
expectedAltName = "Zw" + baseName;
- } else {
+ }
+ else
+ {
expectedDllName = "Zw" + baseName;
expectedAltName = "Nt" + baseName;
}
+
int syscallId = -1;
- if (table0.contains(expectedDllName)) {
+
+ if (table0.contains(expectedDllName))
+ {
syscallId = table0[expectedDllName];
- } else if (table0.contains(expectedAltName)) {
+ }
+ else if (table0.contains(expectedAltName))
+ {
syscallId = table0[expectedAltName];
}
- if (syscallId != -1) {
- for (const QString& line : content) {
+
+ if (syscallId != -1)
+ {
+ for (const QString& line : content)
+ {
updatedLines.append(line);
}
+
updatedLines.append("");
}
}
+
// add versioned functions for additional tables
- for (int tableIdx = 1; tableIdx < numTables; ++tableIdx) {
- if (!syscallTables.contains(tableIdx)) continue;
+ for (int tableIdx = 1; tableIdx < numTables; ++tableIdx)
+ {
+ if (!syscallTables.contains(tableIdx))
+ {
+ continue;
+ }
+
QMap table = syscallTables[tableIdx];
QString expectedDllName, expectedAltName;
- if (syscallMode == "Nt") {
+
+ if (syscallMode == "Nt")
+ {
expectedDllName = "Nt" + baseName;
expectedAltName = "Zw" + baseName;
- } else {
+ }
+ else
+ {
expectedDllName = "Zw" + baseName;
expectedAltName = "Nt" + baseName;
}
+
int syscallId = -1;
- if (table.contains(expectedDllName)) {
+
+ if (table.contains(expectedDllName))
+ {
syscallId = table[expectedDllName];
- } else if (table.contains(expectedAltName)) {
+ }
+ else if (table.contains(expectedAltName))
+ {
syscallId = table[expectedAltName];
}
- if (syscallId != -1) {
- for (const QString& line : content) {
+
+ if (syscallId != -1)
+ {
+ for (const QString& line : content)
+ {
QString versionedName = funcName + QString(QChar('A' + tableIdx - 1));
QString versionedLine = line;
- versionedLine.replace(QRegularExpression(QString(R"(\b%1\b)").arg(QRegularExpression::escape(funcName))), versionedName);
+ versionedLine.replace(QRegularExpression(QString(R"(\b%1\b)")
+ .arg(QRegularExpression::escape(funcName))), versionedName);
updatedLines.append(versionedLine);
}
+
updatedLines.append("");
}
}
}
- if (!updatedLines.isEmpty() && !updatedLines.last().trimmed().isEmpty()) {
+ if (!updatedLines.isEmpty() && !updatedLines.last().trimmed().isEmpty())
+ {
updatedLines.append("");
}
+
bool hasExternClose = false;
int searchWindow = qMin(50, updatedLines.size());
QString tail;
- for (int i = qMax(0, updatedLines.size() - searchWindow); i < updatedLines.size(); ++i) {
+
+ for (int i = qMax(0, updatedLines.size() - searchWindow); i < updatedLines.size(); ++i)
+ {
tail += updatedLines[i];
}
+
QRegularExpression externCloseRegex(R"(#ifdef\s+__cplusplus[\s\S]*?\}\s*\n\s*#endif)");
- if (!externCloseRegex.match(tail).hasMatch()) {
+
+ if (!externCloseRegex.match(tail).hasMatch())
+ {
updatedLines.append("");
updatedLines.append("#ifdef __cplusplus");
updatedLines.append("}");
updatedLines.append("#endif");
updatedLines.append("");
}
+
int externOpenIdx = -1;
- for (int i = 0; i < updatedLines.size(); ++i) {
- if (updatedLines[i].trimmed().startsWith("extern \"C\" {")) {
+
+ for (int i = 0; i < updatedLines.size(); ++i)
+ {
+ if (updatedLines[i].trimmed().startsWith("extern \"C\" {"))
+ {
externOpenIdx = i;
break;
}
}
- if (externOpenIdx != -1) {
+
+ if (externOpenIdx != -1)
+ {
bool foundClose = false;
- for (int lookAhead = 1; lookAhead <= 5; ++lookAhead) {
- if (externOpenIdx + lookAhead < updatedLines.size()) {
- if (updatedLines[externOpenIdx + lookAhead].trimmed().startsWith("#endif")) {
+
+ for (int lookAhead = 1; lookAhead <= 5; ++lookAhead)
+ {
+ if (externOpenIdx + lookAhead < updatedLines.size())
+ {
+ if (updatedLines[externOpenIdx + lookAhead].trimmed().startsWith("#endif"))
+ {
foundClose = true;
break;
}
}
}
- if (!foundClose) {
+
+ if (!foundClose)
+ {
updatedLines.insert(externOpenIdx + 1, "#endif");
}
}
+
bool nonEmptyEndingFound = false;
QStringList filteredEndingLines;
- for (const QString& line : endingLines) {
- if (!line.trimmed().isEmpty() || nonEmptyEndingFound) {
+
+ for (const QString& line : endingLines)
+ {
+ if (!line.trimmed().isEmpty() || nonEmptyEndingFound)
+ {
filteredEndingLines.append(line);
nonEmptyEndingFound = true;
}
}
+
updatedLines.append(filteredEndingLines);
+
QStringList cleanedLines;
bool prevEmpty = false;
- for (const QString& line : updatedLines) {
- if (line.trimmed().isEmpty()) {
- if (!prevEmpty) {
+
+ for (const QString& line : updatedLines)
+ {
+ if (line.trimmed().isEmpty())
+ {
+ if (!prevEmpty)
+ {
cleanedLines.append(line);
prevEmpty = true;
}
- } else {
+ }
+ else
+ {
cleanedLines.append(line);
prevEmpty = false;
}
}
- while (!cleanedLines.isEmpty() && cleanedLines.last().trimmed().isEmpty()) {
+
+ while (!cleanedLines.isEmpty() && cleanedLines.last().trimmed().isEmpty())
+ {
cleanedLines.removeLast();
}
+
cleanedLines.append("");
updatedLines = cleanedLines;
+
QFile outFile(headerFilePath);
- if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (outFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
QTextStream outStream(&outFile);
- for (const QString& line : updatedLines) {
+
+ for (const QString& line : updatedLines)
+ {
outStream << line << "\n";
}
+
outFile.close();
}
+
outputProgress(Colors::OKGREEN() + "Updated Header File with Versioned Syscall Declarations" + Colors::ENDC());
}
-void Validator::updateDefFile(const QStringList& syscallNames, const QString& defPath) {
+void Validator::updateDefFile(const QStringList& syscallNames, const QString& defPath)
+{
QFile file(defPath);
- if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (file.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
QTextStream stream(&file);
stream << "LIBRARY SysCaller\n";
stream << "EXPORTS\n";
- for (const QString& name : syscallNames) {
+
+ for (const QString& name : syscallNames)
+ {
stream << " " << name << "\n";
}
+
file.close();
}
}
-QString Validator::getIniPath() {
+QString Validator::getIniPath()
+{
return PathUtils::getIniPath();
}
-QString Validator::getHeaderFilePath(bool isKernelMode) {
+QString Validator::getHeaderFilePath(bool isKernelMode)
+{
return PathUtils::getSysFunctionsPath(isKernelMode);
}
-QString Validator::getAsmFilePath(bool isKernelMode) {
+QString Validator::getAsmFilePath(bool isKernelMode)
+{
return PathUtils::getSysCallerAsmPath(isKernelMode);
}
-QString Validator::getDefFilePath() {
+QString Validator::getDefFilePath()
+{
return PathUtils::getProjectRoot() + "/SysCaller/Wrapper/SysCaller.def";
}
-QString Validator::generateIndirectStub(const QString& stubName, int syscallId) {
+QString Validator::generateIndirectStub(const QString& stubName, int syscallId)
+{
QString baseName = stubName.mid(11);
QString ntName = "Nt" + baseName;
+
QString indirectStub = QString("%1 PROC\n"
" ; Save non volatile registers\n"
" push rbx\n"
@@ -785,5 +1183,6 @@ QString Validator::generateIndirectStub(const QString& stubName, int syscallId)
"%2_str db \"%2\", 0")
.arg(stubName)
.arg(ntName);
+
return indirectStub;
}
diff --git a/Bind/src/Core/Integrity/Verification/Verification.cpp b/Bind/src/Core/Integrity/Verification/Verification.cpp
index 7585a61..c434500 100644
--- a/Bind/src/Core/Integrity/Verification/Verification.cpp
+++ b/Bind/src/Core/Integrity/Verification/Verification.cpp
@@ -7,41 +7,65 @@
#include
#include
#include
+#include
-Verification::Verification() : QObject(nullptr), pe(nullptr), imageBase(0) {}
+Verification::Verification()
+ : QObject(nullptr)
+ , pe(nullptr)
+ , imageBase(0)
+{}
-void Verification::setOutputCallback(std::function callback) {
+void Verification::setOutputCallback(std::function callback)
+{
outputCallback = callback;
}
-void Verification::outputProgress(const QString& message) {
- if (outputCallback) {
+void Verification::outputProgress(const QString& message)
+{
+ if (outputCallback)
+ {
outputCallback(message);
}
}
-int Verification::run(int argc, char* argv[]) {
+int Verification::run(int argc, char* argv[])
+{
return runWithDllPaths(QStringList() << "C:\\Windows\\System32\\ntdll.dll");
}
-int Verification::runWithDllPaths(const QStringList& dllPaths) {
- qDebug() << QString("Verification::runWithDllPaths() called with paths: %1").arg(dllPaths.join(", "));
+int Verification::runWithDllPaths(const QStringList& dllPaths)
+{
+ qDebug() << QString("Verification::runWithDllPaths() called with paths: %1")
+ .arg(dllPaths.join(", "));
+
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
bool isKernelMode = (syscallMode == "Zw");
- qDebug() << QString("Syscall Mode: %1, Kernel Mode: %2").arg(syscallMode).arg(isKernelMode);
+
+ qDebug() << QString("Syscall Mode: %1, Kernel Mode: %2")
+ .arg(syscallMode)
+ .arg(isKernelMode);
+
QStringList dllPathsToUse = dllPaths;
- if (dllPathsToUse.isEmpty()) {
+
+ if (dllPathsToUse.isEmpty())
+ {
dllPathsToUse << "C:\\Windows\\System32\\ntdll.dll";
}
+
this->dllPaths = dllPathsToUse;
this->dllPath = dllPathsToUse.first();
+
qDebug() << QString("Using DLL Paths: %1").arg(dllPathsToUse.join(", "));
+
outputProgress(Colors::OKBLUE() + QString("Starting Verification Check...") + Colors::ENDC());
- outputProgress(Colors::OKBLUE() + QString("Using DLL Paths: %1").arg(dllPathsToUse.join(", ")) + Colors::ENDC());
+ outputProgress(Colors::OKBLUE() + QString("Using DLL Paths: %1")
+ .arg(dllPathsToUse.join(", ")) + Colors::ENDC());
+
qDebug() << "Parsing Header Files for Type Definitions...";
typeTracker.parseHeaderFiles();
qDebug() << "Header Files parsed successfully.";
+
runTests();
return 0;
}
@@ -63,38 +87,55 @@ Verification::TypeDefinitionTracker::TypeDefinitionTracker() : isKernelMode(fals
<< "PTOKEN_MANDATORY_POLICY" << "PCWNF_STATE_NAME" << "PCWNF_TYPE_ID" << "WAIT_TYPE" << "PIO_APC_ROUTINE";
}
-void Verification::TypeDefinitionTracker::parseHeaderFiles() {
+void Verification::TypeDefinitionTracker::parseHeaderFiles()
+{
QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
isKernelMode = (syscallMode == "Zw");
+
QString basePath = PathUtils::getProjectRoot();
QMap headerFiles;
- if (isKernelMode) {
+
+ if (isKernelMode)
+ {
headerFiles["constants"] = basePath + "/SysCallerK/Wrapper/include/SysK/sysConstants_k.h";
headerFiles["types"] = basePath + "/SysCallerK/Wrapper/include/SysK/sysTypes_k.h";
headerFiles["externals"] = basePath + "/SysCallerK/Wrapper/include/SysK/sysExternals_k.h";
- } else {
+ }
+ else
+ {
headerFiles["constants"] = basePath + "/SysCaller/Wrapper/include/Sys/sysConstants.h";
headerFiles["types"] = basePath + "/SysCaller/Wrapper/include/Sys/sysTypes.h";
headerFiles["externals"] = basePath + "/SysCaller/Wrapper/include/Sys/sysExternals.h";
}
- for (auto it = headerFiles.begin(); it != headerFiles.end(); ++it) {
+
+ for (auto it = headerFiles.begin(); it != headerFiles.end(); ++it)
+ {
QString fileType = it.key();
QString filepath = it.value();
+
QFile file(filepath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
qWarning() << "Failed to open Header File:" << filepath;
continue;
}
+
QString content = QTextStream(&file).readAll();
file.close();
- if (fileType == "constants") {
+
+ if (fileType == "constants")
+ {
QRegularExpression defineRegex(R"(#define\s+(\w+)\s+(.+))");
QRegularExpressionMatchIterator matches = defineRegex.globalMatch(content);
- while (matches.hasNext()) {
+
+ while (matches.hasNext())
+ {
QRegularExpressionMatch match = matches.next();
QString name = match.captured(1);
QString value = match.captured(2);
+
TypeDefinition def;
def.file = QString("sysConstants%1.h").arg(isKernelMode ? "_k" : "");
def.definition = QString("#define %1 %2").arg(name).arg(value);
@@ -104,16 +145,20 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse comma types
QRegularExpression commaRegex(R"(}\s*(\w+),\s*\*\s*(\w+);)");
QRegularExpressionMatchIterator commaMatches = commaRegex.globalMatch(content);
- while (commaMatches.hasNext()) {
+
+ while (commaMatches.hasNext())
+ {
QRegularExpressionMatch match = commaMatches.next();
QString baseType = match.captured(1);
QString ptrType = match.captured(2);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def1;
def1.file = fileName;
def1.definition = QString("typedef struct %1").arg(baseType);
typeDefinitions.insert(baseType, def1);
-
+
TypeDefinition def2;
def2.file = fileName;
def2.definition = QString("typedef %1* %2").arg(baseType).arg(ptrType);
@@ -122,11 +167,15 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse pointer types
QRegularExpression ptrRegex(R"(typedef\s+(?:struct\s+)?(?:_)?(\w+)\s*\*\s*(\w+);)");
QRegularExpressionMatchIterator ptrMatches = ptrRegex.globalMatch(content);
- while (ptrMatches.hasNext()) {
+
+ while (ptrMatches.hasNext())
+ {
QRegularExpressionMatch match = ptrMatches.next();
QString baseType = match.captured(1);
QString ptrType = match.captured(2);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def;
def.file = fileName;
def.definition = QString("typedef %1* %2").arg(baseType).arg(ptrType);
@@ -135,11 +184,15 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse basic types
QRegularExpression basicRegex(R"(typedef\s+(?:struct\s+)?(?:_)?(\w+)\s+(\w+);)");
QRegularExpressionMatchIterator basicMatches = basicRegex.globalMatch(content);
- while (basicMatches.hasNext()) {
+
+ while (basicMatches.hasNext())
+ {
QRegularExpressionMatch match = basicMatches.next();
QString baseType = match.captured(1);
QString newType = match.captured(2);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def;
def.file = fileName;
def.definition = QString("typedef %1 %2").arg(baseType).arg(newType);
@@ -148,16 +201,20 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse structs
QRegularExpression structRegex(R"(typedef\s+struct\s+(?:_)?(\w+)\s*\{[^}]+\}\s*(\w+)\s*,\s*\*\s*(\w+);)");
QRegularExpressionMatchIterator structMatches = structRegex.globalMatch(content);
- while (structMatches.hasNext()) {
+
+ while (structMatches.hasNext())
+ {
QRegularExpressionMatch match = structMatches.next();
QString structName = match.captured(2);
QString ptrName = match.captured(3);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def1;
def1.file = fileName;
def1.definition = match.captured(0);
typeDefinitions.insert(structName, def1);
-
+
TypeDefinition def2;
def2.file = fileName;
def2.definition = QString("typedef %1* %2").arg(structName).arg(ptrName);
@@ -166,10 +223,14 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse enums
QRegularExpression enumRegex(R"(typedef\s+enum\s+(?:_)?(\w+)\s*\{[^}]+\}\s*(\w+);)");
QRegularExpressionMatchIterator enumMatches = enumRegex.globalMatch(content);
- while (enumMatches.hasNext()) {
+
+ while (enumMatches.hasNext())
+ {
QRegularExpressionMatch match = enumMatches.next();
QString enumName = match.captured(2);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def;
def.file = fileName;
def.definition = match.captured(0);
@@ -178,10 +239,14 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse function pointers
QRegularExpression funcPtrRegex(R"(typedef\s+\w+\s*\(\s*\w+\s*\*\s*(\w+)\s*\)\s*\([^)]*\))");
QRegularExpressionMatchIterator funcPtrMatches = funcPtrRegex.globalMatch(content);
- while (funcPtrMatches.hasNext()) {
+
+ while (funcPtrMatches.hasNext())
+ {
QRegularExpressionMatch match = funcPtrMatches.next();
QString typeName = match.captured(1);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def;
def.file = fileName;
def.definition = QString("typedef function_ptr %1").arg(typeName);
@@ -190,11 +255,15 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse const pointer types
QRegularExpression constPtrRegex(R"(typedef\s+const\s+(\w+)\s*\*\s*(\w+);)");
QRegularExpressionMatchIterator constPtrMatches = constPtrRegex.globalMatch(content);
- while (constPtrMatches.hasNext()) {
+
+ while (constPtrMatches.hasNext())
+ {
QRegularExpressionMatch match = constPtrMatches.next();
QString baseType = match.captured(1);
QString newType = match.captured(2);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def;
def.file = fileName;
def.definition = QString("typedef const %1* %2").arg(baseType).arg(newType);
@@ -203,61 +272,81 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
// parse WNF types
QRegularExpression wnfRegex(R"(typedef\s+(?:const\s+)?(?:struct\s+)?_?(\w+)\s*(?:\*\s*)?(\w+)(?:\s*,\s*\*\s*(\w+))?;)");
QRegularExpressionMatchIterator wnfMatches = wnfRegex.globalMatch(content);
- while (wnfMatches.hasNext()) {
+
+ while (wnfMatches.hasNext())
+ {
QRegularExpressionMatch match = wnfMatches.next();
QString baseType = match.captured(1);
QString newType = match.captured(2);
QString ptrType = match.captured(3);
- QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1)).arg(isKernelMode ? "_k" : "");
+ QString fileName = QString("sys%1%2.h").arg(fileType.at(0).toUpper() + fileType.mid(1))
+ .arg(isKernelMode ? "_k" : "");
+
TypeDefinition def1;
def1.file = fileName;
def1.definition = QString("typedef %1 %2").arg(baseType).arg(newType);
typeDefinitions.insert(newType, def1);
- if (!ptrType.isEmpty()) {
+
+ if (!ptrType.isEmpty())
+ {
TypeDefinition def2;
def2.file = fileName;
def2.definition = QString("typedef %1* %2").arg(newType).arg(ptrType);
typeDefinitions.insert(ptrType, def2);
}
}
- QStringList commonTypes = {
+
+ QStringList commonTypes =
+ {
"HANDLE", "PVOID", "BOOLEAN", "ULONG", "PULONG", "ACCESS_MASK",
"PHANDLE", "PACCESS_MASK", "PBOOLEAN", "VOID",
"ULONG_PTR", "PULONG_PTR", "ULONG64", "PULONG64",
"UCHAR", "PUCHAR"
};
- for (const QString& typeName : commonTypes) {
+
+ for (const QString& typeName : commonTypes)
+ {
TypeDefinition def;
def.file = QString("sysTypes%1.h").arg(isKernelMode ? "_k" : "");
def.definition = QString("typedef base %1").arg(typeName);
typeDefinitions.insert(typeName, def);
}
}
- if (!typeDefinitions.contains("WNF_CHANGE_STAMP")) {
+
+ if (!typeDefinitions.contains("WNF_CHANGE_STAMP"))
+ {
TypeDefinition def;
def.file = QString("sysExternals%1.h").arg(isKernelMode ? "_k" : "");
def.definition = "typedef ULONG WNF_CHANGE_STAMP";
typeDefinitions.insert("WNF_CHANGE_STAMP", def);
}
- if (!typeDefinitions.contains("PCWNF_STATE_NAME")) {
+
+ if (!typeDefinitions.contains("PCWNF_STATE_NAME"))
+ {
TypeDefinition def;
def.file = QString("sysExternals%1.h").arg(isKernelMode ? "_k" : "");
def.definition = "typedef WNF_STATE_NAME* PCWNF_STATE_NAME";
typeDefinitions.insert("PCWNF_STATE_NAME", def);
}
- if (!typeDefinitions.contains("PCWNF_TYPE_ID")) {
+
+ if (!typeDefinitions.contains("PCWNF_TYPE_ID"))
+ {
TypeDefinition def;
def.file = QString("sysExternals%1.h").arg(isKernelMode ? "_k" : "");
def.definition = "typedef WNF_TYPE_ID* PCWNF_TYPE_ID";
typeDefinitions.insert("PCWNF_TYPE_ID", def);
}
- if (!typeDefinitions.contains("WAIT_TYPE")) {
+
+ if (!typeDefinitions.contains("WAIT_TYPE"))
+ {
TypeDefinition def;
def.file = QString("sysExternals%1.h").arg(isKernelMode ? "_k" : "");
def.definition = "typedef enum WAIT_TYPE";
typeDefinitions.insert("WAIT_TYPE", def);
}
- if (!typeDefinitions.contains("PIO_APC_ROUTINE")) {
+
+ if (!typeDefinitions.contains("PIO_APC_ROUTINE"))
+ {
TypeDefinition def;
def.file = QString("sysTypes%1.h").arg(isKernelMode ? "_k" : "");
def.definition = "typedef function_ptr PIO_APC_ROUTINE";
@@ -265,25 +354,36 @@ void Verification::TypeDefinitionTracker::parseHeaderFiles() {
}
}
-std::optional Verification::TypeDefinitionTracker::checkType(const QString& typeName, bool isKernelMode) {
+std::optional Verification::TypeDefinitionTracker::checkType(const QString& typeName, bool isKernelMode)
+{
QString cleanTypeName = typeName.trimmed();
- if (externalTypes.contains(cleanTypeName)) {
+
+ if (externalTypes.contains(cleanTypeName))
+ {
TypeDefinition def;
def.file = isKernelMode ? "Windows WDK" : "Windows SDK";
def.definition = QString("typedef external %1").arg(cleanTypeName);
return def;
}
- if (cleanTypeName.startsWith("const ")) {
+
+ if (cleanTypeName.startsWith("const "))
+ {
cleanTypeName = cleanTypeName.mid(6);
}
- if (cleanTypeName.contains(" *")) {
+
+ if (cleanTypeName.contains(" *"))
+ {
cleanTypeName = cleanTypeName.replace(" *", "*");
QString baseType = cleanTypeName.left(cleanTypeName.length() - 1);
QString ptrType = "P" + baseType;
- if (typeDefinitions.contains(ptrType)) {
+
+ if (typeDefinitions.contains(ptrType))
+ {
return typeDefinitions[ptrType];
}
- if (typeDefinitions.contains(cleanTypeName)) {
+
+ if (typeDefinitions.contains(cleanTypeName))
+ {
return typeDefinitions[cleanTypeName];
}
}
@@ -369,88 +469,136 @@ std::optional Verification::TypeDefinitionTracker:
return std::nullopt;
}
-void Verification::parseSyscallDefinitions() {
+void Verification::parseSyscallDefinitions()
+{
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
bool isKernelMode = (syscallMode == "Zw");
QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK";
+
QString headerPath = getHeaderFilePath(isKernelMode);
QString asmPath = getAsmFilePath(isKernelMode);
+
qDebug() << QString("Parsing Syscall Definitions from: %1").arg(headerPath);
+
QFile file(headerPath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
qWarning() << "Failed to open Header File:" << headerPath;
outputProgress(Colors::FAIL() + QString("Failed to open Header File: %1").arg(headerPath) + Colors::ENDC());
return;
}
+
QString content = QTextStream(&file).readAll();
file.close();
+
QRegularExpression externCRegex(R"(#ifdef\s+__cplusplus\s+extern\s+"C"\s+\{)");
bool hasExternC = externCRegex.match(content).hasMatch();
+
QString pattern1 = QString(R"(extern\s*"C"\s*((?:NTSTATUS|ULONG|BOOLEAN|VOID))\s+((?:SC|%1)\w+)\s*\(([\s\S]*?)\)\s*;)").arg(syscallPrefix);
QString pattern2 = QString(R"(((?:NTSTATUS|ULONG|BOOLEAN|VOID))\s+((?:SC|%1)\w+)\s*\(([\s\S]*?)\)\s*;)").arg(syscallPrefix);
+
QRegularExpression regex1(pattern1);
QRegularExpression regex2(pattern2);
+
QRegularExpressionMatchIterator matches = regex1.globalMatch(content);
QList matchesList;
- while (matches.hasNext()) {
+
+ while (matches.hasNext())
+ {
matchesList.append(matches.next());
}
- if (matchesList.isEmpty() || hasExternC) {
+
+ if (matchesList.isEmpty() || hasExternC)
+ {
matches = regex2.globalMatch(content);
matchesList.clear();
- while (matches.hasNext()) {
+
+ while (matches.hasNext())
+ {
matchesList.append(matches.next());
}
}
+
qDebug() << QString("Parsing Syscall Offsets from: %1").arg(asmPath);
QMap offsets = parseSyscallOffsets(asmPath);
qDebug() << QString("Found %1 Syscall Definitions").arg(matchesList.size());
- for (const QRegularExpressionMatch& match : matchesList) {
+
+ for (const QRegularExpressionMatch& match : matchesList)
+ {
QString returnType = match.captured(1);
QString name = match.captured(2);
- if (name.startsWith("SC")) {
+
+ if (name.startsWith("SC"))
+ {
name = syscallPrefix + name.mid(2);
}
+
QString paramsStr = match.captured(3).trimmed();
QList params;
- if (!paramsStr.isEmpty() && paramsStr.toUpper() != "VOID") {
+
+ if (!paramsStr.isEmpty() && paramsStr.toUpper() != "VOID")
+ {
QStringList paramList = paramsStr.split(',');
- for (const QString& param : paramList) {
+
+ for (const QString& param : paramList)
+ {
QString cleanParam = param.trimmed();
int commentPos = cleanParam.indexOf("//");
- if (commentPos != -1) {
+
+ if (commentPos != -1)
+ {
cleanParam = cleanParam.left(commentPos);
}
+
commentPos = cleanParam.indexOf("/*");
- if (commentPos != -1) {
+
+ if (commentPos != -1)
+ {
cleanParam = cleanParam.left(commentPos);
}
+
cleanParam = cleanParam.trimmed();
+
if (cleanParam.isEmpty()) continue;
+
bool isOptional = cleanParam.contains("OPTIONAL");
QString paramType = cleanParam.replace("OPTIONAL", "").trimmed();
- if (!paramType.isEmpty()) {
+
+ if (!paramType.isEmpty())
+ {
QStringList paramParts = paramType.split(' ', Qt::SkipEmptyParts);
- if (!paramParts.isEmpty()) {
+
+ if (!paramParts.isEmpty())
+ {
QString paramName = paramParts.last();
paramType = paramParts.mid(0, paramParts.size() - 1).join(' ');
commentPos = paramType.indexOf("//");
- if (commentPos != -1) {
+
+ if (commentPos != -1)
+ {
paramType = paramType.left(commentPos);
}
+
commentPos = paramType.indexOf("/*");
- if (commentPos != -1) {
+
+ if (commentPos != -1)
+ {
paramType = paramType.left(commentPos);
}
+
paramType = paramType.trimmed();
- if (!paramType.isEmpty()) {
+
+ if (!paramType.isEmpty())
+ {
params.append({paramType, paramName, isOptional});
}
}
}
}
}
+
QString offset = offsets.value(name, "Unknown");
SyscallDefinition def;
def.name = name;
@@ -460,103 +608,156 @@ void Verification::parseSyscallDefinitions() {
def.description = "";
syscalls[name] = def;
}
+
qDebug() << QString("Successfully parsed %1 Syscall Definitions").arg(syscalls.size());
}
-QMap Verification::parseSyscallOffsets(const QString& asmPath) {
+QMap Verification::parseSyscallOffsets(const QString& asmPath)
+{
QMap offsets;
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK";
+
QFile file(asmPath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
qWarning() << "Failed to open ASM File:" << asmPath;
outputProgress(Colors::FAIL() + QString("Failed to open ASM File: %1").arg(asmPath) + Colors::ENDC());
return offsets;
}
+
QString content = QTextStream(&file).readAll();
file.close();
+
QString pattern = QString(R"(((?:SC|Sys|SysK)\w+)\s+PROC[\s\S]*?mov\s+eax,\s+([\dA-Fa-fh]+))");
QRegularExpression regex(pattern);
QRegularExpressionMatchIterator matches = regex.globalMatch(content);
int offsetCount = 0;
- while (matches.hasNext()) {
+
+ while (matches.hasNext())
+ {
QRegularExpressionMatch match = matches.next();
QString name = match.captured(1);
QString offset = match.captured(2);
- if (name.startsWith("SC")) {
+
+ if (name.startsWith("SC"))
+ {
name = syscallPrefix + name.mid(2);
}
+
offsets[name] = offset;
offsetCount++;
}
+
outputProgress(QString("Found %1 Syscall Offsets in ASM File").arg(offsetCount));
return offsets;
}
-std::optional Verification::getOffsetFromDll(const QString& syscallName, const QString& dllPath) {
+std::optional Verification::getOffsetFromDll(const QString& syscallName, const QString& dllPath)
+{
QString dllPathToUse = dllPath.isEmpty() ? this->dllPath : dllPath;
+
qDebug() << QString("Getting Offset for %1 from DLL: %2").arg(syscallName).arg(dllPathToUse);
+
QByteArray dllPathBytes = dllPathToUse.toLocal8Bit();
const char* dllPathCStr = dllPathBytes.constData();
+
pe = peparse::ParsePEFromFile(dllPathCStr);
- if (!pe) {
+
+ if (!pe)
+ {
qWarning() << "Failed to parse PE File:" << dllPathToUse;
return std::nullopt;
}
+
imageBase = pe->peHeader.nt.OptionalHeader64.ImageBase;
QString primaryName, secondaryName;
- if (syscallName.startsWith("SysK")) {
+
+ if (syscallName.startsWith("SysK"))
+ {
primaryName = "Nt" + syscallName.mid(4);
secondaryName = "Zw" + syscallName.mid(4);
- } else if (syscallName.startsWith("Sys")) {
+ }
+ else if (syscallName.startsWith("Sys"))
+ {
primaryName = "Nt" + syscallName.mid(3);
secondaryName = "Zw" + syscallName.mid(3);
- } else {
+ }
+ else
+ {
primaryName = syscallName;
secondaryName = syscallName;
}
+
std::optional result = std::nullopt;
syscallNumbers.clear();
- peparse::IterExpVA(pe, [](void* N, const peparse::VA& addr, const std::string& mod, const std::string& fn) -> int {
+
+ peparse::IterExpVA(pe, [](void* N, const peparse::VA& addr, const std::string& mod, const std::string& fn) -> int
+ {
auto* verification = static_cast(N);
+
// safety check for the callback parameters
- if (!verification || fn.empty()) {
+ if (!verification || fn.empty())
+ {
return 0;
}
+
// use a safer string conversion
QString funcName;
- try {
+
+ try
+ {
funcName = QString::fromUtf8(fn.c_str(), fn.length());
- } catch (...) {
+ }
+ catch (...)
+ {
return 0;
}
- if (!funcName.startsWith("Nt") && !funcName.startsWith("Zw")) {
+
+ if (!funcName.startsWith("Nt") && !funcName.startsWith("Zw"))
+ {
return 0;
}
+
// get function RVA (addr is VA, subtract image base to get RVA)
uint32_t funcRVA = static_cast(addr - verification->imageBase);
uint32_t fileOffset = 0;
+
// safety check for RVA calculation
- if (addr < verification->imageBase) {
+ if (addr < verification->imageBase)
+ {
return 0;
}
- if (SyscallExtractor::rvaToFileOffset(verification->pe, funcRVA, fileOffset)) {
- if (!verification->pe || !verification->pe->fileBuffer) {
+
+ if (SyscallExtractor::rvaToFileOffset(verification->pe, funcRVA, fileOffset))
+ {
+ if (!verification->pe || !verification->pe->fileBuffer)
+ {
return 0;
}
+
std::vector funcBytes;
size_t bytesRead = SyscallExtractor::readBytesFromBuffer(verification->pe->fileBuffer, fileOffset, 32, funcBytes);
- if (bytesRead > 0) {
- if (bytesRead >= 8) {
- for (size_t i = 0; i <= bytesRead - 8; ++i) {
- if (funcBytes[i] == 0x4c && funcBytes[i+1] == 0x8b && funcBytes[i+2] == 0xd1) {
- if (funcBytes[i+3] == 0xb8) {
- uint32_t syscallId = funcBytes[i+4] |
- (funcBytes[i+5] << 8) |
- (funcBytes[i+6] << 16) |
+
+ if (bytesRead > 0)
+ {
+ if (bytesRead >= 8)
+ {
+ for (size_t i = 0; i <= bytesRead - 8; ++i)
+ {
+ if (funcBytes[i] == 0x4c && funcBytes[i+1] == 0x8b && funcBytes[i+2] == 0xd1)
+ {
+ if (funcBytes[i+3] == 0xb8)
+ {
+ uint32_t syscallId = funcBytes[i+4] |
+ (funcBytes[i+5] << 8) |
+ (funcBytes[i+6] << 16) |
(funcBytes[i+7] << 24);
- if (syscallId <= 0xFFFF) {
+
+ if (syscallId <= 0xFFFF)
+ {
verification->syscallNumbers[funcName] = static_cast(syscallId);
break;
}
@@ -566,88 +767,132 @@ std::optional Verification::getOffsetFromDll(const QString& syscallName, co
}
}
}
+
return 0;
}, this);
- if (syscallNumbers.contains(primaryName)) {
+
+ if (syscallNumbers.contains(primaryName))
+ {
result = syscallNumbers[primaryName];
- } else if (syscallNumbers.contains(secondaryName)) {
+ }
+ else if (syscallNumbers.contains(secondaryName))
+ {
result = syscallNumbers[secondaryName];
}
+
peparse::DestructParsedPE(pe);
return result;
}
-Verification::TestResult Verification::testSyscall(const SyscallDefinition& syscall) {
+Verification::TestResult Verification::testSyscall(const SyscallDefinition& syscall)
+{
QSettings settings(getIniPath(), QSettings::IniFormat);
QString syscallMode = settings.value("general/syscall_mode", "Nt").toString();
bool isKernelMode = (syscallMode == "Zw");
+
TestResult result;
result.name = syscall.name;
result.status = "SUCCESS";
result.offset = syscall.offset;
result.returnType = syscall.returnType;
result.parameterCount = syscall.parameters.size();
+
QRegularExpression versionRegex(R"((?:Sys|SysK)(\w+?)(\d+)?$)");
QRegularExpressionMatch versionMatch = versionRegex.match(syscall.name);
QString dllPathToUse = dllPath;
- if (versionMatch.hasMatch() && !versionMatch.captured(2).isEmpty()) {
+
+ if (versionMatch.hasMatch() && !versionMatch.captured(2).isEmpty())
+ {
int version = versionMatch.captured(2).toInt();
- if (version > 1 && dllPaths.size() >= version) {
+
+ if (version > 1 && dllPaths.size() >= version)
+ {
dllPathToUse = dllPaths[version - 1];
}
}
+
// validate return type
QStringList validReturnTypes = {"NTSTATUS", "BOOL", "HANDLE", "VOID", "ULONG", "ULONG_PTR", "UINT32", "UINT64"};
- if (!validReturnTypes.contains(syscall.returnType)) {
+
+ if (!validReturnTypes.contains(syscall.returnType))
+ {
result.errors.append(QString("Unexpected return type: %1").arg(syscall.returnType));
}
+
// validate parameters
- for (const Parameter& param : syscall.parameters) {
- if (!validateParameterType(param.type)) {
+ for (const Parameter& param : syscall.parameters)
+ {
+ if (!validateParameterType(param.type))
+ {
result.errors.append(QString("Invalid Parameter Type: %1").arg(param.type));
}
}
+
// validate offset
QString offset = syscall.offset.toLower().replace("h", "");
bool ok;
int offsetValue = offset.toInt(&ok, 16);
- if (ok) {
- if (offsetValue > 0x0200) {
+
+ if (ok)
+ {
+ if (offsetValue > 0x0200)
+ {
result.errors.append(QString("Suspicious Syscall Offset: 0x%1 (expected range: 0x0000-0x0200)").arg(offset));
}
+
std::optional expectedOffset = getOffsetFromDll(syscall.name, dllPathToUse);
- if (expectedOffset.has_value() && expectedOffset.value() != offsetValue) {
+
+ if (expectedOffset.has_value() && expectedOffset.value() != offsetValue)
+ {
result.errors.append(QString("Offset Mismatch: Got 0x%1, Expected 0x%2")
.arg(offset)
.arg(QString::number(expectedOffset.value(), 16).toUpper()));
}
- } else {
+ }
+ else
+ {
result.errors.append(QString("Invalid Syscall Offset Format: %1").arg(syscall.offset));
}
+
// check type definitions
- for (const Parameter& param : syscall.parameters) {
+ for (const Parameter& param : syscall.parameters)
+ {
std::optional typeInfo = typeTracker.checkType(param.type, isKernelMode);
- if (!typeInfo.has_value()) {
+
+ if (!typeInfo.has_value())
+ {
result.errors.append(QString("Type '%1' not found in Header Files").arg(param.type));
- } else {
+ }
+ else
+ {
result.typeDefinitions.append({param.type, typeInfo.value().file});
}
}
- if (!result.errors.isEmpty()) {
+
+ if (!result.errors.isEmpty())
+ {
result.status = "FAILED";
}
+
return result;
}
-bool Verification::validateParameterType(const QString& paramType) {
+bool Verification::validateParameterType(const QString& paramType)
+{
QString cleanParamType = paramType.trimmed();
- if (cleanParamType.isEmpty()) {
+
+ if (cleanParamType.isEmpty())
+ {
return false;
}
- if (cleanParamType.startsWith("const ")) {
+
+ if (cleanParamType.startsWith("const "))
+ {
cleanParamType = cleanParamType.mid(6);
}
- QStringList validTypes = {
+
+ QStringList validTypes =
+ {
"HANDLE", "PHANDLE", "PVOID", "ULONG", "PULONG", "BOOLEAN",
"POBJECT_ATTRIBUTES", "ACCESS_MASK", "PCLIENT_ID", "PLARGE_INTEGER",
"PPORT_MESSAGE", "PPORT_VIEW", "PREMOTE_PORT_VIEW",
@@ -714,51 +959,76 @@ bool Verification::validateParameterType(const QString& paramType) {
"PWORKER_FACTORY_INFORMATION", "PDBGUI_WAIT_STATE_CHANGE", "PWAIT_TYPE", "PLOGICAL",
"PFILE_PATH_INFORMATION", "PFILE_NETWORK_OPEN_INFORMATION"
};
- if (cleanParamType.endsWith("*")) {
+
+ if (cleanParamType.endsWith("*"))
+ {
QString baseType = cleanParamType.left(cleanParamType.length() - 1).trimmed();
QString pointerType = "P" + baseType;
+
return (validTypes.contains(baseType) || validTypes.contains(pointerType) ||
- std::any_of(validTypes.begin(), validTypes.end(), [&baseType](const QString& validType) {
+ std::any_of(validTypes.begin(), validTypes.end(), [&baseType](const QString& validType)
+ {
return baseType.contains(validType);
}));
}
- if (cleanParamType.contains("[")) {
+
+ if (cleanParamType.contains("["))
+ {
cleanParamType = cleanParamType.left(cleanParamType.indexOf("[")).trimmed();
}
- if (cleanParamType.startsWith("LP")) {
+
+ if (cleanParamType.startsWith("LP"))
+ {
QString altType = "P" + cleanParamType.mid(2);
+
return (validTypes.contains(cleanParamType) || validTypes.contains(altType) ||
- std::any_of(validTypes.begin(), validTypes.end(), [&cleanParamType](const QString& validType) {
+ std::any_of(validTypes.begin(), validTypes.end(), [&cleanParamType](const QString& validType)
+ {
return cleanParamType.contains(validType);
}));
}
+
return (validTypes.contains(cleanParamType) ||
- std::any_of(validTypes.begin(), validTypes.end(), [&cleanParamType](const QString& validType) {
+ std::any_of(validTypes.begin(), validTypes.end(), [&cleanParamType](const QString& validType)
+ {
return cleanParamType.contains(validType);
}));
}
-void Verification::runTests(const QString& outputFormat) {
+void Verification::runTests(const QString& outputFormat)
+{
parseSyscallDefinitions();
outputProgress(Colors::BOLD() + QString("Testing %1 Syscalls...").arg(syscalls.size()) + Colors::ENDC());
outputProgress("");
- for (auto it = syscalls.begin(); it != syscalls.end(); ++it) {
- try {
+
+ for (auto it = syscalls.begin(); it != syscalls.end(); ++it)
+ {
+ try
+ {
TestResult result = testSyscall(it.value());
testResults.append(result);
printResult(result);
- } catch (const std::exception& e) {
+ }
+ catch (const std::exception& e)
+ {
outputProgress(Colors::FAIL() + QString("Error Testing %1: %2").arg(it.key()).arg(e.what()) + Colors::ENDC());
}
}
+
int successCount = 0, failureCount = 0;
- for (const TestResult& result : testResults) {
- if (result.status == "SUCCESS") {
+
+ for (const TestResult& result : testResults)
+ {
+ if (result.status == "SUCCESS")
+ {
successCount++;
- } else {
+ }
+ else
+ {
failureCount++;
}
}
+
outputProgress("");
outputProgress(Colors::BOLD() + "Verification Summary:" + Colors::ENDC());
outputProgress(QString("Total Syscalls Tested: %1").arg(testResults.size()));
@@ -767,29 +1037,40 @@ void Verification::runTests(const QString& outputFormat) {
outputProgress("");
}
-void Verification::printResult(const TestResult& result) {
+void Verification::printResult(const TestResult& result)
+{
bool useAscii = false;
QMap treeChars;
- if (useAscii) {
+
+ if (useAscii)
+ {
treeChars["branch"] = "|--";
treeChars["last"] = "`--";
treeChars["indent"] = " ";
- } else {
+ }
+ else
+ {
treeChars["branch"] = "├─";
treeChars["last"] = "└─";
treeChars["indent"] = " ";
}
+
outputProgress(Colors::BOLD() + result.name + Colors::ENDC());
QString statusColor = (result.status == "SUCCESS") ? Colors::OKGREEN() : Colors::FAIL();
outputProgress(QString("%1 Status: %2%3%4").arg(treeChars["branch"]).arg(statusColor).arg(result.status).arg(Colors::ENDC()));
outputProgress(QString("%1 Offset: 0x%2").arg(treeChars["branch"]).arg(result.offset.toLower().replace("h", "")));
outputProgress(QString("%1 Return Type: %2").arg(treeChars["branch"]).arg(result.returnType));
outputProgress(QString("%1 Parameters: %2").arg(treeChars["last"]).arg(result.parameterCount));
- if (!result.typeDefinitions.isEmpty()) {
+
+ if (!result.typeDefinitions.isEmpty())
+ {
outputProgress(QString("%1|-- Type Definitions:").arg(treeChars["indent"]));
- for (int i = 0; i < result.typeDefinitions.size(); ++i) {
+
+ for (int i = 0; i < result.typeDefinitions.size(); ++i)
+ {
bool isLast = (i == result.typeDefinitions.size() - 1) && result.errors.isEmpty();
QString prefix = isLast ? treeChars["last"] : treeChars["branch"];
+
outputProgress(QString("%1 %2 %3: %4%5%6")
.arg(treeChars["indent"])
.arg(prefix)
@@ -799,10 +1080,15 @@ void Verification::printResult(const TestResult& result) {
.arg(Colors::ENDC()));
}
}
- if (!result.errors.isEmpty()) {
+
+ if (!result.errors.isEmpty())
+ {
outputProgress(QString("%1|-- Errors:").arg(treeChars["indent"]));
- for (int i = 0; i < result.errors.size(); ++i) {
+
+ for (int i = 0; i < result.errors.size(); ++i)
+ {
QString prefix = (i == result.errors.size() - 1) ? treeChars["last"] : treeChars["branch"];
+
outputProgress(QString("%1 %2 %3%4%5")
.arg(treeChars["indent"])
.arg(prefix)
@@ -811,17 +1097,21 @@ void Verification::printResult(const TestResult& result) {
.arg(Colors::ENDC()));
}
}
+
outputProgress("");
}
-QString Verification::getIniPath() {
+QString Verification::getIniPath()
+{
return PathUtils::getIniPath();
}
-QString Verification::getAsmFilePath(bool isKernelMode) {
+QString Verification::getAsmFilePath(bool isKernelMode)
+{
return PathUtils::getSysCallerAsmPath(isKernelMode);
}
-QString Verification::getHeaderFilePath(bool isKernelMode) {
+QString Verification::getHeaderFilePath(bool isKernelMode)
+{
return PathUtils::getSysFunctionsPath(isKernelMode);
}
\ No newline at end of file
diff --git a/Bind/src/Core/Obfuscation/Direct/ControlFlow/DirectControlFlow.cpp b/Bind/src/Core/Obfuscation/Direct/ControlFlow/DirectControlFlow.cpp
index 351e9a2..68c3246 100644
--- a/Bind/src/Core/Obfuscation/Direct/ControlFlow/DirectControlFlow.cpp
+++ b/Bind/src/Core/Obfuscation/Direct/ControlFlow/DirectControlFlow.cpp
@@ -2,30 +2,41 @@
#include
#include
-DirectObfuscation::ControlFlow::ControlFlow(QSettings* settings) : settings(settings) {
-}
+DirectObfuscation::ControlFlow::ControlFlow(QSettings* settings)
+ : settings(settings)
+{}
-void DirectObfuscation::ControlFlow::setSettings(QSettings* settings) {
+void DirectObfuscation::ControlFlow::setSettings(QSettings* settings)
+{
this->settings = settings;
}
-QString DirectObfuscation::ControlFlow::generateRandomLabel(const QString& prefix) {
+QString DirectObfuscation::ControlFlow::generateRandomLabel(const QString& prefix)
+{
QString label;
- do {
+
+ do
+ {
QString suffix = QString::number(getRandomInt(1000, 999999));
label = prefix + suffix;
} while (usedLabels.contains(label));
+
usedLabels.insert(label);
return label;
}
-QStringList DirectObfuscation::ControlFlow::generateOpaquePredicates(const QString& labelPrefix) {
- if (!settings || !isOpaquePredicatesEnabled()) {
+QStringList DirectObfuscation::ControlFlow::generateOpaquePredicates(const QString& labelPrefix)
+{
+ if (!settings || !isOpaquePredicatesEnabled())
+ {
return QStringList();
}
+
QStringList predicates;
int complexity = getControlFlowComplexity();
- for (int i = 0; i < complexity; ++i) {
+
+ for (int i = 0; i < complexity; ++i)
+ {
QString label = generateRandomLabel(labelPrefix);
QString endLabel = generateRandomLabel(labelPrefix);
QStringList predicate = generateComplexPredicate();
@@ -34,16 +45,22 @@ QStringList DirectObfuscation::ControlFlow::generateOpaquePredicates(const QStri
predicates << QString(" jmp %1").arg(endLabel);
predicates << QString("%1:").arg(endLabel);
}
+
return predicates;
}
-QStringList DirectObfuscation::ControlFlow::generateBogusControlFlow(const QString& labelPrefix) {
- if (!settings || !isBogusControlFlowEnabled()) {
+QStringList DirectObfuscation::ControlFlow::generateBogusControlFlow(const QString& labelPrefix)
+{
+ if (!settings || !isBogusControlFlowEnabled())
+ {
return QStringList();
}
+
QStringList bogusFlow;
int complexity = getControlFlowComplexity();
- for (int i = 0; i < complexity; ++i) {
+
+ for (int i = 0; i < complexity; ++i)
+ {
QString label1 = generateRandomLabel(labelPrefix);
QString label2 = generateRandomLabel(labelPrefix);
QString reg = getRandomRegister();
@@ -56,32 +73,44 @@ QStringList DirectObfuscation::ControlFlow::generateBogusControlFlow(const QStri
bogusFlow << QString(" jmp %2").arg(label2);
bogusFlow << QString("%2:").arg(label2);
}
+
return bogusFlow;
}
-QStringList DirectObfuscation::ControlFlow::generateIndirectJumps(const QString& labelPrefix) {
- if (!settings || !isIndirectJumpsEnabled()) {
+QStringList DirectObfuscation::ControlFlow::generateIndirectJumps(const QString& labelPrefix)
+{
+ if (!settings || !isIndirectJumpsEnabled())
+ {
return QStringList();
}
+
QStringList indirectJumps;
int complexity = getControlFlowComplexity();
- for (int i = 0; i < complexity; ++i) {
+
+ for (int i = 0; i < complexity; ++i)
+ {
QString targetLabel = generateRandomLabel(labelPrefix);
QString tempReg = getRandomRegister();
indirectJumps << QString(" lea %1, %2").arg(tempReg).arg(targetLabel);
indirectJumps << QString(" jmp %1").arg(tempReg);
indirectJumps << QString("%1:").arg(targetLabel);
}
+
return indirectJumps;
}
-QStringList DirectObfuscation::ControlFlow::generateConditionalBranches(const QString& labelPrefix) {
- if (!settings || !isConditionalBranchesEnabled()) {
+QStringList DirectObfuscation::ControlFlow::generateConditionalBranches(const QString& labelPrefix)
+{
+ if (!settings || !isConditionalBranchesEnabled())
+ {
return QStringList();
}
+
QStringList branches;
int complexity = getControlFlowComplexity();
- for (int i = 0; i < complexity; ++i) {
+
+ for (int i = 0; i < complexity; ++i)
+ {
QString trueLabel = generateRandomLabel(labelPrefix);
QString falseLabel = generateRandomLabel(labelPrefix);
QString endLabel = generateRandomLabel(labelPrefix);
@@ -96,73 +125,98 @@ QStringList DirectObfuscation::ControlFlow::generateConditionalBranches(const QS
branches << QString(" jmp %3").arg(endLabel);
branches << QString("%3:").arg(endLabel);
}
+
return branches;
}
-QStringList DirectObfuscation::ControlFlow::wrapWithControlFlow(const QStringList& originalCode, const QString& labelPrefix) {
- if (!settings) {
+QStringList DirectObfuscation::ControlFlow::wrapWithControlFlow(const QStringList& originalCode,
+ const QString& labelPrefix)
+{
+ if (!settings)
+ {
return originalCode;
}
+
QStringList obfuscatedCode;
- if (isOpaquePredicatesEnabled()) {
+
+ if (isOpaquePredicatesEnabled())
+ {
obfuscatedCode << generateOpaquePredicates(labelPrefix);
}
- if (isBogusControlFlowEnabled()) {
+
+ if (isBogusControlFlowEnabled())
+ {
obfuscatedCode << generateBogusControlFlow(labelPrefix);
}
+
obfuscatedCode << originalCode;
- if (isIndirectJumpsEnabled()) {
+
+ if (isIndirectJumpsEnabled())
+ {
obfuscatedCode << generateIndirectJumps(labelPrefix);
}
- if (isConditionalBranchesEnabled()) {
+
+ if (isConditionalBranchesEnabled())
+ {
obfuscatedCode << generateConditionalBranches(labelPrefix);
}
+
return obfuscatedCode;
}
-bool DirectObfuscation::ControlFlow::isOpaquePredicatesEnabled() {
+bool DirectObfuscation::ControlFlow::isOpaquePredicatesEnabled()
+{
return settings ? settings->value("obfuscation/control_flow_opaque_predicates", false).toBool() : false;
}
-bool DirectObfuscation::ControlFlow::isBogusControlFlowEnabled() {
+bool DirectObfuscation::ControlFlow::isBogusControlFlowEnabled()
+{
return settings ? settings->value("obfuscation/control_flow_bogus_flow", false).toBool() : false;
}
-bool DirectObfuscation::ControlFlow::isIndirectJumpsEnabled() {
+bool DirectObfuscation::ControlFlow::isIndirectJumpsEnabled()
+{
return settings ? settings->value("obfuscation/control_flow_indirect_jumps", false).toBool() : false;
}
-bool DirectObfuscation::ControlFlow::isConditionalBranchesEnabled() {
+bool DirectObfuscation::ControlFlow::isConditionalBranchesEnabled()
+{
return settings ? settings->value("obfuscation/control_flow_conditional_branches", false).toBool() : false;
}
-int DirectObfuscation::ControlFlow::getControlFlowComplexity() {
+int DirectObfuscation::ControlFlow::getControlFlowComplexity()
+{
return settings ? settings->value("obfuscation/control_flow_complexity", 2).toInt() : 2;
}
-int DirectObfuscation::ControlFlow::getRandomInt(int min, int max) {
+int DirectObfuscation::ControlFlow::getRandomInt(int min, int max)
+{
return QRandomGenerator::global()->bounded(min, max + 1);
}
-QString DirectObfuscation::ControlFlow::getRandomRegister() {
+QString DirectObfuscation::ControlFlow::getRandomRegister()
+{
QStringList registers = {"rax", "rbx", "rcx", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
return registers[getRandomInt(0, registers.size() - 1)];
}
-QString DirectObfuscation::ControlFlow::getRandomCondition() {
+QString DirectObfuscation::ControlFlow::getRandomCondition()
+{
QStringList conditions = {"test", "cmp", "and", "or", "xor"};
return conditions[getRandomInt(0, conditions.size() - 1)];
}
-QStringList DirectObfuscation::ControlFlow::generateAlwaysTrueCondition() {
+QStringList DirectObfuscation::ControlFlow::generateAlwaysTrueCondition()
+{
QStringList conditions;
- QString reg = getRandomRegister();
+ QString reg = getRandomRegister();
conditions << QString(" mov %1, 1").arg(reg);
conditions << QString(" test %1, %1").arg(reg);
return conditions;
}
-QStringList DirectObfuscation::ControlFlow::generateAlwaysFalseCondition() {
+QStringList DirectObfuscation::ControlFlow::generateAlwaysFalseCondition()
+{
QStringList conditions;
QString reg = getRandomRegister();
conditions << QString(" mov %1, 0").arg(reg);
@@ -170,7 +224,8 @@ QStringList DirectObfuscation::ControlFlow::generateAlwaysFalseCondition() {
return conditions;
}
-QStringList DirectObfuscation::ControlFlow::generateComplexPredicate() {
+QStringList DirectObfuscation::ControlFlow::generateComplexPredicate()
+{
QStringList predicate;
QString reg1 = getRandomRegister();
QString reg2 = getRandomRegister();
diff --git a/Bind/src/Core/Obfuscation/Direct/Encryption/DirectEncryptor.cpp b/Bind/src/Core/Obfuscation/Direct/Encryption/DirectEncryptor.cpp
index 77112ef..a1ce626 100644
--- a/Bind/src/Core/Obfuscation/Direct/Encryption/DirectEncryptor.cpp
+++ b/Bind/src/Core/Obfuscation/Direct/Encryption/DirectEncryptor.cpp
@@ -2,34 +2,53 @@
#include
#include
-DirectObfuscation::Encryptor::Encryptor(QSettings* settings) : settings(settings) {
-}
+DirectObfuscation::Encryptor::Encryptor(QSettings* settings)
+ : settings(settings)
+{}
-void DirectObfuscation::Encryptor::setSettings(QSettings* settings) {
+void DirectObfuscation::Encryptor::setSettings(QSettings* settings)
+{
this->settings = settings;
}
-int DirectObfuscation::Encryptor::getEncryptionMethod() {
- if (!settings) {
- return 1;
+DirectObfuscation::EncryptionMethod DirectObfuscation::Encryptor::getEncryptionMethod()
+{
+ if (!settings)
+ {
+ return DirectObfuscation::EncryptionMethod::BasicXOR;
}
- return settings->value("obfuscation/encryption_method", 1).toInt();
+
+ int methodValue = settings->value("obfuscation/encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt();
+ return static_cast(methodValue);
}
-QPair> DirectObfuscation::Encryptor::encryptOffset(int realOffset, int method) {
- if (method == -1) {
- method = getEncryptionMethod();
+QPair> DirectObfuscation::Encryptor::encryptOffset(int realOffset, int method)
+{
+ DirectObfuscation::EncryptionMethod encryptionMethod = DirectObfuscation::EncryptionMethod::BasicXOR;
+
+ if (method == -1)
+ {
+ encryptionMethod = getEncryptionMethod();
}
+ else
+ {
+ encryptionMethod = static_cast(method);
+ }
+
QMap encryptionData;
int encryptedOffset;
- switch (method) {
- case 1: { // basic xor
+
+ switch (encryptionMethod)
+ {
+ case DirectObfuscation::EncryptionMethod::BasicXOR:
+ {
int key = getRandomInt(0x11, 0xFF);
encryptedOffset = realOffset ^ key;
encryptionData["key"] = key;
break;
}
- case 2: { // multi key xor
+ case DirectObfuscation::EncryptionMethod::MultiKeyXOR:
+ {
int key1 = getRandomInt(0x11, 0xFF);
int key2 = getRandomInt(0x11, 0xFF);
encryptedOffset = (realOffset ^ key1) ^ key2;
@@ -37,7 +56,8 @@ QPair> DirectObfuscation::Encryptor::encryptOffset(
encryptionData["key2"] = key2;
break;
}
- case 3: { // add + xor combo
+ case DirectObfuscation::EncryptionMethod::AddXORCombo:
+ {
int addVal = getRandomInt(0x100, 0xFFF);
int xorKey = getRandomInt(0x11, 0xFF);
encryptedOffset = (realOffset + addVal) ^ xorKey;
@@ -45,42 +65,61 @@ QPair> DirectObfuscation::Encryptor::encryptOffset(
encryptionData["xor_key"] = xorKey;
break;
}
- case 4: { // enhanced xor
+ case DirectObfuscation::EncryptionMethod::EnhancedXOR:
+ {
int xorKey = getRandomInt(0x1000, 0xFFFF);
encryptedOffset = realOffset ^ xorKey;
encryptionData["xor_key"] = xorKey;
break;
}
- case 5: { // offset shifting
+ case DirectObfuscation::EncryptionMethod::OffsetShifting:
+ {
int mask = getRandomInt(0x100, 0xFFF);
encryptedOffset = (realOffset + mask) & 0xFFFFFFFF;
encryptionData["mask"] = mask;
break;
}
- default: { // default to basic xor
+ default: // default to basic xor
+ {
int key = getRandomInt(0x11, 0xFF);
encryptedOffset = realOffset ^ key;
encryptionData["key"] = key;
break;
}
}
+
return qMakePair(encryptedOffset, encryptionData);
}
-QStringList DirectObfuscation::Encryptor::generateDecryptionSequence(const QString& offsetName, const QMap& encryptionData, int method) {
- if (method == -1) {
- method = getEncryptionMethod();
+QStringList DirectObfuscation::Encryptor::generateDecryptionSequence(const QString& offsetName,
+ const QMap& encryptionData,
+ int method)
+{
+ DirectObfuscation::EncryptionMethod decryptionMethod = DirectObfuscation::EncryptionMethod::BasicXOR;
+
+ if (method == -1)
+ {
+ decryptionMethod = getEncryptionMethod();
+ }
+ else
+ {
+ decryptionMethod = static_cast(method);
}
+
QStringList sequence;
- switch (method) {
- case 1: { // basic xor
+
+ switch (decryptionMethod)
+ {
+ case DirectObfuscation::EncryptionMethod::BasicXOR:
+ {
int key = encryptionData["key"].toInt();
sequence << QString(" mov eax, dword ptr [%1]\n").arg(offsetName);
sequence << QString(" mov ebx, 0%1h\n").arg(key, 0, 16);
sequence << " xor eax, ebx\n";
break;
}
- case 2: { // multi key xor
+ case DirectObfuscation::EncryptionMethod::MultiKeyXOR:
+ {
int key1 = encryptionData["key1"].toInt();
int key2 = encryptionData["key2"].toInt();
sequence << QString(" mov eax, dword ptr [%1]\n").arg(offsetName);
@@ -90,7 +129,8 @@ QStringList DirectObfuscation::Encryptor::generateDecryptionSequence(const QStri
sequence << " xor eax, ebx\n";
break;
}
- case 3: { // add + xor combo
+ case DirectObfuscation::EncryptionMethod::AddXORCombo:
+ {
int xorKey = encryptionData["xor_key"].toInt();
int addVal = encryptionData["add_val"].toInt();
sequence << QString(" mov eax, dword ptr [%1]\n").arg(offsetName);
@@ -99,20 +139,23 @@ QStringList DirectObfuscation::Encryptor::generateDecryptionSequence(const QStri
sequence << QString(" sub eax, 0%1h\n").arg(addVal, 0, 16);
break;
}
- case 4: { // enhanced xor
+ case DirectObfuscation::EncryptionMethod::EnhancedXOR:
+ {
int xorKey = encryptionData["xor_key"].toInt();
sequence << QString(" mov eax, dword ptr [%1]\n").arg(offsetName);
sequence << QString(" mov ebx, 0%1h\n").arg(xorKey, 0, 16);
sequence << " xor eax, ebx\n";
break;
}
- case 5: { // offset shifting
+ case DirectObfuscation::EncryptionMethod::OffsetShifting:
+ {
int mask = encryptionData["mask"].toInt();
sequence << QString(" mov eax, dword ptr [%1]\n").arg(offsetName);
sequence << QString(" sub eax, 0%1h\n").arg(mask, 0, 16);
break;
}
- default: { // default to basic xor
+ default: // default to basic xor
+ {
int key = encryptionData["key"].toInt();
sequence << QString(" mov eax, dword ptr [%1]\n").arg(offsetName);
sequence << QString(" mov ebx, 0%1h\n").arg(key, 0, 16);
@@ -120,9 +163,11 @@ QStringList DirectObfuscation::Encryptor::generateDecryptionSequence(const QStri
break;
}
}
+
return sequence;
}
-int DirectObfuscation::Encryptor::getRandomInt(int min, int max) {
+int DirectObfuscation::Encryptor::getRandomInt(int min, int max)
+{
return QRandomGenerator::global()->bounded(min, max + 1);
}
diff --git a/Bind/src/Core/Obfuscation/Direct/Mapping/DirectStubMapper.cpp b/Bind/src/Core/Obfuscation/Direct/Mapping/DirectStubMapper.cpp
index dda8009..7323f25 100644
--- a/Bind/src/Core/Obfuscation/Direct/Mapping/DirectStubMapper.cpp
+++ b/Bind/src/Core/Obfuscation/Direct/Mapping/DirectStubMapper.cpp
@@ -13,84 +13,122 @@
#include
#include
-DirectObfuscation::StubMapper::StubMapper(QSettings* settings) : settings(settings), outputCallback(nullptr) {
-}
+DirectObfuscation::StubMapper::StubMapper(QSettings* settings)
+ : settings(settings)
+ , outputCallback(nullptr)
+{}
-void DirectObfuscation::StubMapper::setSettings(QSettings* settings) {
+void DirectObfuscation::StubMapper::setSettings(QSettings* settings)
+{
this->settings = settings;
}
-void DirectObfuscation::StubMapper::setOutputCallback(std::function callback) {
+void DirectObfuscation::StubMapper::setOutputCallback(std::function callback)
+{
outputCallback = callback;
}
-void DirectObfuscation::StubMapper::logMessage(const QString& message) {
- if (outputCallback) {
+void DirectObfuscation::StubMapper::logMessage(const QString& message)
+{
+ if (outputCallback)
+ {
outputCallback(message);
}
+
qDebug() << "StubMapper:" << message;
}
-int DirectObfuscation::StubMapper::extractSyscallOffset(const QString& line) {
+int DirectObfuscation::StubMapper::extractSyscallOffset(const QString& line)
+{
QRegularExpression regex(R"(mov eax,\s*([0-9A-Fa-f]+)h)");
QRegularExpressionMatch match = regex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString offsetStr = match.captured(1);
bool ok;
int offset = offsetStr.toInt(&ok, 16);
- if (ok) {
+
+ if (ok)
+ {
return offset;
}
}
+
return -1;
}
-QString DirectObfuscation::StubMapper::getAsmFilePath(bool isKernelMode) {
- if (isKernelMode) {
+QString DirectObfuscation::StubMapper::getAsmFilePath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return PathUtils::getSysCallerKPath() + "/Wrapper/src/syscaller.asm";
- } else {
+ }
+ else
+ {
return PathUtils::getSysCallerPath() + "/Wrapper/src/syscaller.asm";
}
}
-QString DirectObfuscation::StubMapper::getHeaderFilePath(bool isKernelMode) {
- if (isKernelMode) {
+QString DirectObfuscation::StubMapper::getHeaderFilePath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return PathUtils::getSysCallerKPath() + "/Wrapper/include/SysK/sysFunctions_k.h";
- } else {
+ }
+ else
+ {
return PathUtils::getSysCallerPath() + "/Wrapper/include/Sys/sysFunctions.h";
}
}
-QString DirectObfuscation::StubMapper::getDefFilePath(bool isKernelMode) {
- if (isKernelMode) {
+QString DirectObfuscation::StubMapper::getDefFilePath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return PathUtils::getSysCallerKPath() + "/Wrapper/SysCallerK.def";
- } else {
+ }
+ else
+ {
return PathUtils::getSysCallerPath() + "/Wrapper/SysCaller.def";
}
}
-bool DirectObfuscation::StubMapper::isKernelMode() {
+bool DirectObfuscation::StubMapper::isKernelMode()
+{
return settings->value("general/syscall_mode", "Nt").toString() == "Zw";
}
-QString DirectObfuscation::StubMapper::getSyscallPrefix() {
+QString DirectObfuscation::StubMapper::getSyscallPrefix()
+{
return isKernelMode() ? "SysK" : "Sys";
}
-QPair DirectObfuscation::StubMapper::applyCustomSyscallSettings(const QString& syscallName, int realOffset, const QMap& customSettings) {
+QPair DirectObfuscation::StubMapper::applyCustomSyscallSettings(
+ const QString& syscallName,
+ int realOffset,
+ const QMap& customSettings)
+{
QMap settings;
-
- if (customSettings.isEmpty()) {
- QMap syscallSettings = this->settings->value("stub_mapper/syscall_settings", QMap()).toMap();
- if (syscallSettings.contains(syscallName)) {
+
+ if (customSettings.isEmpty())
+ {
+ QMap syscallSettings = this->settings->value(
+ "stub_mapper/syscall_settings",
+ QMap()).toMap();
+
+ if (syscallSettings.contains(syscallName))
+ {
settings = syscallSettings[syscallName].toMap();
- } else {
+ }
+ else
+ {
settings["enable_junk"] = true;
settings["min_instructions"] = this->settings->value("obfuscation/min_instructions", 2).toInt();
settings["max_instructions"] = this->settings->value("obfuscation/max_instructions", 8).toInt();
settings["use_advanced_junk"] = this->settings->value("obfuscation/use_advanced_junk", false).toBool();
settings["enable_encryption"] = this->settings->value("obfuscation/enable_encryption", true).toBool();
- settings["encryption_method"] = this->settings->value("obfuscation/encryption_method", 1).toInt();
+ settings["encryption_method"] = static_cast(DirectObfuscation::EncryptionMethod::BasicXOR);
settings["enable_chunking"] = this->settings->value("obfuscation/enable_chunking", true).toBool();
settings["enable_interleaved"] = this->settings->value("obfuscation/enable_interleaved", true).toBool();
settings["shuffle_sequence"] = this->settings->value("obfuscation/shuffle_sequence", true).toBool();
@@ -104,110 +142,163 @@ QPair DirectObfuscation::StubMapper::applyCustomSyscallSettings(co
settings["control_flow_conditional_branches"] = this->settings->value("obfuscation/control_flow_conditional_branches", false).toBool();
settings["control_flow_complexity"] = this->settings->value("obfuscation/control_flow_complexity", 2).toInt();
}
- } else {
+ }
+ else
+ {
settings = customSettings;
}
+
QSet usedOffsets;
QSet usedOffsetNames;
+
SharedObfuscation::NameGenerator nameGen(this->settings);
Encryptor encryptor(this->settings);
+
int fakeOffset = nameGen.generateRandomOffset(usedOffsets);
int offsetNameLength = settings.value("offset_name_length", 8).toInt();
QString offsetName = nameGen.generateRandomOffsetName(usedOffsetNames, offsetNameLength);
+
return qMakePair(fakeOffset, offsetName);
}
-bool DirectObfuscation::StubMapper::generateCustomExports() {
+bool DirectObfuscation::StubMapper::generateCustomExports()
+{
logMessage(Colors::OKBLUE() + "Starting Stub Mapper Custom Export Generation..." + Colors::ENDC());
- try {
+
+ try
+ {
bool success = processAssemblyFile(getAsmFilePath(isKernelMode()), getHeaderFilePath(isKernelMode()));
- if (success) {
+
+ if (success)
+ {
logMessage(Colors::OKGREEN() + "Stub Mapper Custom Export Generation Completed Successfully!" + Colors::ENDC());
return true;
- } else {
+ }
+ else
+ {
logMessage(Colors::FAIL() + "Stub Mapper Custom Export Generation Failed!" + Colors::ENDC());
return false;
}
- } catch (const std::exception& e) {
+ }
+ catch (const std::exception& e)
+ {
logMessage(Colors::FAIL() + QString("Stub Mapper Error: %1").arg(e.what()) + Colors::ENDC());
return false;
}
}
-bool DirectObfuscation::StubMapper::processAssemblyFile(const QString& asmPath, const QString& headerPath) {
+bool DirectObfuscation::StubMapper::processAssemblyFile(const QString& asmPath, const QString& headerPath)
+{
QFile asmFile(asmPath);
- if (!asmFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!asmFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to open Assembly File: %1").arg(asmPath) + Colors::ENDC());
return false;
}
+
QTextStream in(&asmFile);
QStringList content = in.readAll().split('\n');
asmFile.close();
+
QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList();
bool useAllSyscalls = selectedSyscalls.isEmpty();
QString syscallPrefix = getSyscallPrefix();
+
QSet usedNames;
QSet usedOffsets;
QSet usedOffsetNames;
- QMap offsetNameMap; // maps fake offset to random name
- QMap syscallMap; // maps original syscall to random name
- QMap syscallOffsets; // maps original syscall to its offset
- QMap realToFakeOffset; // maps real offset to fake offset
+
+ QMap offsetNameMap; // maps fake offset to random name
+ QMap syscallMap; // maps original syscall to random name
+ QMap syscallOffsets; // maps original syscall to its offset
+ QMap realToFakeOffset; // maps real offset to fake offset
+
QList> syscallStubs;
QStringList currentStub;
QString currentSyscall;
bool inStub = false;
+
QMap syscallSettings = settings->value("stub_mapper/syscall_settings", QMap()).toMap();
- for (const QString& line : content) {
+ for (const QString& line : content)
+ {
QRegularExpression procRegex(R"((SC\w+|Sys\w+|SysK\w+)\s+PROC)");
QRegularExpressionMatch procMatch = procRegex.match(line);
- if (procMatch.hasMatch()) {
+
+ if (procMatch.hasMatch())
+ {
currentSyscall = procMatch.captured(1);
- if (currentSyscall.startsWith("SC")) {
+
+ if (currentSyscall.startsWith("SC"))
+ {
currentSyscall = syscallPrefix + currentSyscall.mid(2);
}
+
inStub = true;
currentStub.clear();
currentStub << line;
- if (useAllSyscalls || selectedSyscalls.contains(currentSyscall)) {
- if (!syscallMap.contains(currentSyscall)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentSyscall))
+ {
+ if (!syscallMap.contains(currentSyscall))
+ {
SharedObfuscation::NameGenerator nameGen(settings);
int prefixLength = 8;
int numberLength = 6;
- if (syscallSettings.contains(currentSyscall)) {
+
+ if (syscallSettings.contains(currentSyscall))
+ {
QMap customSettings = syscallSettings[currentSyscall].toMap();
prefixLength = customSettings.value("syscall_prefix_length", 8).toInt();
numberLength = customSettings.value("syscall_number_length", 6).toInt();
}
+
syscallMap[currentSyscall] = nameGen.generateRandomName(usedNames, prefixLength, numberLength);
}
}
- } else if (inStub) {
+ }
+ else if (inStub)
+ {
currentStub << line;
- if (line.contains("mov eax,") && !currentSyscall.isEmpty()) {
+
+ if (line.contains("mov eax,") && !currentSyscall.isEmpty())
+ {
int realOffset = extractSyscallOffset(line);
- if (realOffset != -1) {
+
+ if (realOffset != -1)
+ {
syscallOffsets[currentSyscall] = realOffset;
- if (syscallSettings.contains(currentSyscall)) {
+
+ if (syscallSettings.contains(currentSyscall))
+ {
QMap customSettings = syscallSettings[currentSyscall].toMap();
auto result = applyCustomSyscallSettings(currentSyscall, realOffset, customSettings);
+
int fakeOffset = result.first;
QString offsetName = result.second;
+
realToFakeOffset[realOffset] = fakeOffset;
offsetNameMap[fakeOffset] = offsetName;
usedOffsets.insert(fakeOffset);
usedOffsetNames.insert(offsetName);
- } else if (!realToFakeOffset.contains(realOffset)) {
+ }
+ else if (!realToFakeOffset.contains(realOffset))
+ {
SharedObfuscation::NameGenerator nameGen(settings);
int fakeOffset = nameGen.generateRandomOffset(usedOffsets);
QString offsetName = nameGen.generateRandomOffsetName(usedOffsetNames);
+
realToFakeOffset[realOffset] = fakeOffset;
offsetNameMap[fakeOffset] = offsetName;
}
}
- } else if (line.contains(" ENDP")) {
+ }
+ else if (line.contains(" ENDP"))
+ {
inStub = false;
- if (useAllSyscalls || selectedSyscalls.contains(currentSyscall)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentSyscall))
+ {
syscallStubs.append(qMakePair(currentSyscall, currentStub));
}
}
@@ -216,245 +307,380 @@ bool DirectObfuscation::StubMapper::processAssemblyFile(const QString& asmPath,
bool globalShuffle = settings->value("obfuscation/shuffle_sequence", true).toBool();
QList> syscallsToShuffle;
QList> syscallsToKeepOrder;
- for (const auto& stubPair : syscallStubs) {
+
+ for (const auto& stubPair : syscallStubs)
+ {
QString syscall = stubPair.first;
- if (syscallSettings.contains(syscall) && syscallSettings[syscall].toMap().contains("shuffle_sequence")) {
- if (syscallSettings[syscall].toMap()["shuffle_sequence"].toBool()) {
+
+ if (syscallSettings.contains(syscall) && syscallSettings[syscall].toMap().contains("shuffle_sequence"))
+ {
+ if (syscallSettings[syscall].toMap()["shuffle_sequence"].toBool())
+ {
syscallsToShuffle.append(stubPair);
- } else {
+ }
+ else
+ {
syscallsToKeepOrder.append(stubPair);
}
- } else if (globalShuffle) {
+ }
+ else if (globalShuffle)
+ {
syscallsToShuffle.append(stubPair);
- } else {
+ }
+ else
+ {
syscallsToKeepOrder.append(stubPair);
}
}
- for (int i = syscallsToShuffle.size() - 1; i > 0; --i) {
+
+ for (int i = syscallsToShuffle.size() - 1; i > 0; --i)
+ {
int j = getRandomInt(0, i);
syscallsToShuffle.swapItemsAt(i, j);
}
+
syscallStubs = syscallsToShuffle + syscallsToKeepOrder;
+
QStringList publics;
QStringList aliases;
bool enableControlFlow = settings->value("obfuscation/control_flow_enabled", false).toBool();
+
QMap functionSuffixes; // store suffixes for each function
- if (enableControlFlow) {
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ if (enableControlFlow)
+ {
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
QString suffix = QString::number(getRandomInt(1000, 999999));
functionSuffixes[it.key()] = suffix;
}
}
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
QString obfuscatedName = it.value();
- if (enableControlFlow && functionSuffixes.contains(it.key())) {
+
+ if (enableControlFlow && functionSuffixes.contains(it.key()))
+ {
obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffixes[it.key()]);
}
+
publics << QString("PUBLIC %1").arg(obfuscatedName);
aliases << QString("ALIAS <%1> = <%2>").arg(it.key()).arg(obfuscatedName);
}
QStringList newContent;
newContent << ".data";
newContent << "ALIGN 8";
+
QMap> encryptionDataMap;
Encryptor encryptor(settings);
SharedObfuscation::NameGenerator nameGen(settings);
- for (auto it = realToFakeOffset.begin(); it != realToFakeOffset.end(); ++it) {
+
+ for (auto it = realToFakeOffset.begin(); it != realToFakeOffset.end(); ++it)
+ {
int realOffset = it.key();
int fakeOffset = it.value();
QString offsetName = offsetNameMap[fakeOffset];
bool enableEncryption = true;
- int encryptionMethod = 1;
- for (auto syscallIt = syscallOffsets.begin(); syscallIt != syscallOffsets.end(); ++syscallIt) {
- if (syscallIt.value() == realOffset) {
+ DirectObfuscation::EncryptionMethod encryptionMethod = DirectObfuscation::EncryptionMethod::BasicXOR;
+
+ for (auto syscallIt = syscallOffsets.begin(); syscallIt != syscallOffsets.end(); ++syscallIt)
+ {
+ if (syscallIt.value() == realOffset)
+ {
QString syscall = syscallIt.key();
- if (syscallSettings.contains(syscall)) {
+
+ if (syscallSettings.contains(syscall))
+ {
QMap customSettings = syscallSettings[syscall].toMap();
enableEncryption = customSettings.value("enable_encryption", true).toBool();
- encryptionMethod = customSettings.value("encryption_method", 1).toInt();
+ encryptionMethod = static_cast(customSettings.value("encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt());
}
+
break;
}
}
- if (enableEncryption) {
- auto encryptionResult = encryptor.encryptOffset(realOffset, encryptionMethod);
+
+ if (enableEncryption)
+ {
+ auto encryptionResult = encryptor.encryptOffset(realOffset, static_cast(encryptionMethod));
int encryptedOffset = encryptionResult.first;
encryptionDataMap[offsetName] = encryptionResult.second;
+
newContent << QString("%1 dd 0%2h ; Encrypted Syscall ID (Method %3)")
- .arg(offsetName).arg(encryptedOffset, 0, 16).arg(encryptionMethod);
- } else {
+ .arg(offsetName)
+ .arg(encryptedOffset, 0, 16)
+ .arg(static_cast(encryptionMethod));
+ }
+ else
+ {
newContent << QString("%1 dd 0%2h").arg(offsetName).arg(realOffset, 0, 16);
}
}
newContent << ".code";
newContent << "";
newContent << "; Public Declarations";
- for (const QString& pub : publics) {
+
+ for (const QString& pub : publics)
+ {
newContent << pub;
}
+
newContent << "";
newContent << "; Export Aliases";
- for (const QString& alias : aliases) {
+
+ for (const QString& alias : aliases)
+ {
newContent << alias;
}
+
newContent << "";
+
StubGenerator stubGen(settings);
ControlFlow controlFlow(settings);
- for (const auto& stubPair : syscallStubs) {
+
+ for (const auto& stubPair : syscallStubs)
+ {
QString originalSyscall = stubPair.first;
QStringList stubLines = stubPair.second;
bool skipRest = false;
QString functionSuffix; // store the random suffix for this function
- if (enableControlFlow && functionSuffixes.contains(originalSyscall)) {
+
+ if (enableControlFlow && functionSuffixes.contains(originalSyscall))
+ {
functionSuffix = functionSuffixes[originalSyscall];
}
- if (enableControlFlow) {
+
+ if (enableControlFlow)
+ {
QString labelPrefix;
- if (syscallMap.contains(originalSyscall)) {
+
+ if (syscallMap.contains(originalSyscall))
+ {
labelPrefix = QString("%1_").arg(syscallMap.value(originalSyscall));
- } else {
+ }
+ else
+ {
labelPrefix = QString("%1_").arg(originalSyscall);
}
+
stubLines = controlFlow.wrapWithControlFlow(stubLines, labelPrefix);
}
bool enableInterleaved = true;
- if (syscallSettings.contains(originalSyscall)) {
+
+ if (syscallSettings.contains(originalSyscall))
+ {
QMap customSettings = syscallSettings[originalSyscall].toMap();
enableInterleaved = customSettings.value("enable_interleaved", true).toBool();
}
- if (enableInterleaved) {
+
+ if (enableInterleaved)
+ {
newContent << stubGen.generateAlignPadding();
}
- for (const QString& originalLine : stubLines) {
- if (skipRest) {
- if (originalLine.contains(" ENDP")) {
+
+ for (const QString& originalLine : stubLines)
+ {
+ if (skipRest)
+ {
+ if (originalLine.contains(" ENDP"))
+ {
QString line = originalLine;
QRegularExpression syscallRegex(R"((SC\w+|Sys\w+|SysK\w+)\s+ENDP)");
QRegularExpressionMatch match = syscallRegex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString syscall = match.captured(1);
- if (syscall.startsWith("SC")) {
+
+ if (syscall.startsWith("SC"))
+ {
syscall = syscallPrefix + syscall.mid(2);
}
- if (syscallMap.contains(syscall)) {
- QString obfuscatedName = syscallMap.value(syscall);
- if (enableControlFlow && !functionSuffix.isEmpty()) {
- obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffix);
+
+ if (syscallMap.contains(syscall))
+ {
+ QString obfuscatedName = syscallMap.value(syscall);
+
+ if (enableControlFlow && !functionSuffix.isEmpty())
+ {
+ obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffix);
+ }
+
+ line = line.replace(match.captured(1), obfuscatedName);
}
- line = line.replace(match.captured(1), obfuscatedName);
- }
}
+
newContent << line;
skipRest = false;
}
+
continue;
}
QString line = originalLine;
- if (line.contains(" PROC") || line.contains(" ENDP")) {
+
+ if (line.contains(" PROC") || line.contains(" ENDP"))
+ {
QRegularExpression syscallRegex(R"((SC\w+|Sys\w+|SysK\w+)\s+(PROC|ENDP))");
QRegularExpressionMatch match = syscallRegex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString syscall = match.captured(1);
- if (syscall.startsWith("SC")) {
+
+ if (syscall.startsWith("SC"))
+ {
syscall = syscallPrefix + syscall.mid(2);
}
- if (syscallMap.contains(syscall)) {
+
+ if (syscallMap.contains(syscall))
+ {
QString obfuscatedName = syscallMap.value(syscall);
- if (enableControlFlow && !functionSuffix.isEmpty()) {
+
+ if (enableControlFlow && !functionSuffix.isEmpty())
+ {
obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffix);
}
+
line = line.replace(match.captured(1), obfuscatedName);
}
}
- } else if (line.contains("mov eax,") && stubLines.join("").contains("syscall")) {
- if (syscallOffsets.contains(originalSyscall)) {
+ }
+ else if (line.contains("mov eax,") && stubLines.join("").contains("syscall"))
+ {
+ if (syscallOffsets.contains(originalSyscall))
+ {
int realOffset = syscallOffsets.value(originalSyscall);
- if (realToFakeOffset.contains(realOffset)) {
+
+ if (realToFakeOffset.contains(realOffset))
+ {
int fakeOffset = realToFakeOffset.value(realOffset);
QString offsetName = offsetNameMap.value(fakeOffset);
QMap encryptionData = encryptionDataMap.value(offsetName);
bool enableChunking = true;
- int encryptionMethod = 1;
- if (syscallSettings.contains(originalSyscall)) {
+ DirectObfuscation::EncryptionMethod encryptionMethod = DirectObfuscation::EncryptionMethod::BasicXOR;
+
+ if (syscallSettings.contains(originalSyscall))
+ {
QMap customSettings = syscallSettings[originalSyscall].toMap();
enableChunking = customSettings.value("enable_chunking", true).toBool();
- encryptionMethod = customSettings.value("encryption_method", 1).toInt();
+ encryptionMethod = static_cast(customSettings.value("encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt());
}
- if (enableChunking) {
- line = stubGen.generateChunkedSequence(offsetName, encryptionData, encryptionMethod);
- } else {
+
+ if (enableChunking)
+ {
+ line = stubGen.generateChunkedSequence(offsetName, encryptionData, static_cast(encryptionMethod));
+ }
+ else
+ {
line = QString(" mov eax, dword ptr [%1]\n").arg(offsetName);
}
+
newContent << line;
skipRest = true;
continue;
}
}
}
- if (syscallSettings.contains(originalSyscall)) {
+ if (syscallSettings.contains(originalSyscall))
+ {
QMap customSettings = syscallSettings[originalSyscall].toMap();
- if (customSettings.value("enable_junk", false).toBool() && (line.contains("ret") || line.contains("syscall"))) {
+
+ if (customSettings.value("enable_junk", false).toBool() &&
+ (line.contains("ret") || line.contains("syscall")))
+ {
int minInst = customSettings.value("min_instructions", 2).toInt();
int maxInst = customSettings.value("max_instructions", 8).toInt();
bool useAdvanced = customSettings.value("use_advanced_junk", false).toBool();
+
JunkGenerator junkGen(settings);
QString junk = junkGen.generateJunkInstructions(minInst, maxInst, useAdvanced);
- if (!junk.isEmpty()) {
+
+ if (!junk.isEmpty())
+ {
newContent << line;
newContent << junk;
continue;
}
}
}
+
newContent << line;
}
- if (enableInterleaved) {
+
+ if (enableInterleaved)
+ {
newContent << stubGen.generateAlignPadding();
}
}
+
newContent << "\nend\n";
+
QFile outAsmFile(asmPath);
- if (!outAsmFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (!outAsmFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to write Assembly File: %1").arg(asmPath) + Colors::ENDC());
return false;
}
+
QTextStream out(&outAsmFile);
out << newContent.join("\n");
outAsmFile.close();
- if (!updateHeaderFile(headerPath, syscallMap, functionSuffixes)) {
+
+ if (!updateHeaderFile(headerPath, syscallMap, functionSuffixes))
+ {
logMessage(Colors::FAIL() + "Failed to update Header File" + Colors::ENDC());
return false;
}
+
bool bindingsEnabled = settings->value("general/bindings_enabled", false).toBool();
- if (bindingsEnabled && !isKernelMode()) {
+
+ if (bindingsEnabled && !isKernelMode())
+ {
QString defPath = getDefFilePath(isKernelMode());
QStringList obfuscatedNames;
QRegularExpression procRegex(R"(\s*([A-Za-z0-9_]+)\s+PROC)");
- for (const QString& line : newContent) {
+
+ for (const QString& line : newContent)
+ {
QRegularExpressionMatch match = procRegex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
obfuscatedNames << match.captured(1);
}
}
- if (!updateDefFile(defPath, obfuscatedNames)) {
+
+ if (!updateDefFile(defPath, obfuscatedNames))
+ {
logMessage(Colors::FAIL() + "Failed to update DEF File" + Colors::ENDC());
return false;
}
}
- logMessage(Colors::OKGREEN() + QString("Generated %1 Unique Syscalls with Custom Obfuscation Settings").arg(syscallMap.size()) + Colors::ENDC());
+
+ logMessage(Colors::OKGREEN() + QString("Generated %1 Unique Syscalls with Custom Obfuscation Settings")
+ .arg(syscallMap.size()) + Colors::ENDC());
logMessage(Colors::OKGREEN() + "Applied Stub Mapper Settings to Syscalls" + Colors::ENDC());
return true;
}
-bool DirectObfuscation::StubMapper::updateHeaderFile(const QString& headerPath, const QMap& syscallMap, const QMap& functionSuffixes) {
+bool DirectObfuscation::StubMapper::updateHeaderFile(const QString& headerPath,
+ const QMap& syscallMap,
+ const QMap& functionSuffixes)
+{
QFile headerFile(headerPath);
- if (!headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!headerFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to open Header File: %1").arg(headerPath) + Colors::ENDC());
return false;
}
+
QTextStream in(&headerFile);
QStringList headerContent = in.readAll().split('\n');
headerFile.close();
+
QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList();
bool useAllSyscalls = selectedSyscalls.isEmpty();
bool enableControlFlow = settings->value("obfuscation/control_flow_enabled", false).toBool();
@@ -463,136 +689,195 @@ bool DirectObfuscation::StubMapper::updateHeaderFile(const QString& headerPath,
bool headerPartEnded = false;
bool skipBlock = false;
QString currentSyscall;
- for (const QString& line : headerContent) {
- if (!headerPartEnded && (
- line.contains(QString("NTSTATUS %1").arg(syscallPrefix)) ||
- line.contains(QString("ULONG %1").arg(syscallPrefix)) ||
- line.contains(QString("BOOLEAN %1").arg(syscallPrefix)) ||
- line.contains(QString("VOID %1").arg(syscallPrefix)) ||
- line.contains("NTSTATUS SC") ||
- line.contains("ULONG SC") ||
- line.contains("BOOLEAN SC") ||
- line.contains("VOID SC") ||
- line.contains("#ifdef __cplusplus")
- )) {
+
+ for (const QString& line : headerContent)
+ {
+ if (!headerPartEnded &&
+ (line.contains(QString("NTSTATUS %1").arg(syscallPrefix)) ||
+ line.contains(QString("ULONG %1").arg(syscallPrefix)) ||
+ line.contains(QString("BOOLEAN %1").arg(syscallPrefix)) ||
+ line.contains(QString("VOID %1").arg(syscallPrefix)) ||
+ line.contains("NTSTATUS SC") ||
+ line.contains("ULONG SC") ||
+ line.contains("BOOLEAN SC") ||
+ line.contains("VOID SC") ||
+ line.contains("#ifdef __cplusplus")))
+ {
headerPartEnded = true;
}
- if (!headerPartEnded) {
- if (line.contains("_WIN64") && line.contains("#ifdef")) {
+
+ if (!headerPartEnded)
+ {
+ if (line.contains("_WIN64") && line.contains("#ifdef"))
+ {
newHeaderContent << line;
newHeaderContent << "";
continue;
}
+
newHeaderContent << line;
continue;
}
// preserve c++ guards and extern blocks
- if (line.contains("#ifdef __cplusplus") || line.contains("extern \"C\"") ||
- line.trimmed() == "{" || line.trimmed() == "}" || line.contains("#endif")) {
+ if (line.contains("#ifdef __cplusplus") ||
+ line.contains("extern \"C\"") ||
+ line.trimmed() == "{" ||
+ line.trimmed() == "}" ||
+ line.contains("#endif"))
+ {
newHeaderContent << line;
continue;
}
+
if (line.contains("NTSTATUS SC") || line.contains("ULONG SC") ||
line.contains("BOOLEAN SC") || line.contains("VOID SC") ||
line.contains(QString("NTSTATUS %1").arg(syscallPrefix)) ||
line.contains(QString("ULONG %1").arg(syscallPrefix)) ||
line.contains(QString("BOOLEAN %1").arg(syscallPrefix)) ||
- line.contains(QString("VOID %1").arg(syscallPrefix))) {
+ line.contains(QString("VOID %1").arg(syscallPrefix)))
+ {
QRegularExpression regex(QString(R"(extern "C" (?:NTSTATUS|ULONG|BOOLEAN|VOID) ((?:SC|%1)\w+)\()").arg(syscallPrefix));
QRegularExpressionMatch match = regex.match(line);
- if (!match.hasMatch()) {
+
+ if (!match.hasMatch())
+ {
regex = QRegularExpression(QString(R"((?:NTSTATUS|ULONG|BOOLEAN|VOID) ((?:SC|%1)\w+)\()").arg(syscallPrefix));
match = regex.match(line);
}
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString originalName = match.captured(1);
- if (originalName.startsWith("SC")) {
+
+ if (originalName.startsWith("SC"))
+ {
currentSyscall = syscallPrefix + originalName.mid(2);
- } else {
+ }
+ else
+ {
currentSyscall = originalName;
}
- if (useAllSyscalls || selectedSyscalls.contains(currentSyscall)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentSyscall))
+ {
skipBlock = false;
- if (syscallMap.contains(currentSyscall)) {
+
+ if (syscallMap.contains(currentSyscall))
+ {
QString newLine = line;
QString obfuscatedName = syscallMap.value(currentSyscall);
- if (enableControlFlow && functionSuffixes.contains(currentSyscall)) {
+
+ if (enableControlFlow && functionSuffixes.contains(currentSyscall))
+ {
obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffixes[currentSyscall]);
}
+
newLine = newLine.replace(originalName, obfuscatedName);
newLine = newLine.replace("extern \"C\" ", "");
newHeaderContent << newLine;
}
- } else {
+ }
+ else
+ {
skipBlock = true;
}
+
continue;
}
}
- if (!skipBlock) {
- if (line.contains("SC")) {
+
+ if (!skipBlock)
+ {
+ if (line.contains("SC"))
+ {
QString updatedLine = line;
QRegularExpression scRegex(R"(\bSC(\w+)\b)");
updatedLine = updatedLine.replace(scRegex, QString("%1\\1").arg(syscallPrefix));
newHeaderContent << updatedLine;
- } else {
+ }
+ else
+ {
newHeaderContent << line;
}
- } else if (line.trimmed() == ");") {
+ }
+ else if (line.trimmed() == ");")
+ {
skipBlock = false;
}
}
newHeaderContent << "";
newHeaderContent << "// Syscall Name Mappings";
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
QString obfuscatedName = it.value();
- if (enableControlFlow && functionSuffixes.contains(it.key())) {
+
+ if (enableControlFlow && functionSuffixes.contains(it.key()))
+ {
obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffixes[it.key()]);
}
+
newHeaderContent << QString("#define %1 %2").arg(it.key()).arg(obfuscatedName);
}
+
QStringList cleanedHeaderContent;
bool prevEmpty = false;
- for (const QString& line : newHeaderContent) {
- if (line.trimmed().isEmpty()) {
- if (!prevEmpty) {
+
+ for (const QString& line : newHeaderContent)
+ {
+ if (line.trimmed().isEmpty())
+ {
+ if (!prevEmpty)
+ {
cleanedHeaderContent << line;
prevEmpty = true;
}
- } else {
+ }
+ else
+ {
cleanedHeaderContent << line;
prevEmpty = false;
}
}
+
QFile outHeaderFile(headerPath);
- if (!outHeaderFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (!outHeaderFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to write Header File: %1").arg(headerPath) + Colors::ENDC());
return false;
}
+
QTextStream out(&outHeaderFile);
out << cleanedHeaderContent.join("\n");
outHeaderFile.close();
return true;
}
-bool DirectObfuscation::StubMapper::updateDefFile(const QString& defPath, const QStringList& obfuscatedNames) {
+bool DirectObfuscation::StubMapper::updateDefFile(const QString& defPath, const QStringList& obfuscatedNames)
+{
QFile defFile(defPath);
- if (!defFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (!defFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to write DEF File: %1").arg(defPath) + Colors::ENDC());
return false;
}
+
QTextStream out(&defFile);
out << "LIBRARY SysCaller\n";
out << "EXPORTS\n";
- for (const QString& name : obfuscatedNames) {
+
+ for (const QString& name : obfuscatedNames)
+ {
out << " " << name << "\n";
}
+
defFile.close();
return true;
}
-int DirectObfuscation::StubMapper::getRandomInt(int min, int max) {
+int DirectObfuscation::StubMapper::getRandomInt(int min, int max)
+{
return QRandomGenerator::global()->bounded(min, max + 1);
}
-
-
diff --git a/Bind/src/Core/Obfuscation/Direct/Stub/DirectJunkGenerator.cpp b/Bind/src/Core/Obfuscation/Direct/Stub/DirectJunkGenerator.cpp
index b16fe49..1448720 100644
--- a/Bind/src/Core/Obfuscation/Direct/Stub/DirectJunkGenerator.cpp
+++ b/Bind/src/Core/Obfuscation/Direct/Stub/DirectJunkGenerator.cpp
@@ -3,27 +3,43 @@
#include
#include
-DirectObfuscation::JunkGenerator::JunkGenerator(QSettings* settings) : settings(settings) {
-}
+DirectObfuscation::JunkGenerator::JunkGenerator(QSettings* settings)
+ : settings(settings)
+{}
-void DirectObfuscation::JunkGenerator::setSettings(QSettings* settings) {
+void DirectObfuscation::JunkGenerator::setSettings(QSettings* settings)
+{
this->settings = settings;
}
-QString DirectObfuscation::JunkGenerator::generateJunkInstructions(int minInst, int maxInst, bool useAdvanced) {
- if (!settings) {
+QString DirectObfuscation::JunkGenerator::generateJunkInstructions(int minInst, int maxInst, bool useAdvanced)
+{
+ // rcx, rdx, r8, r9 are function parameters, NEVER touch these!
+ // rbx, rsi, rdi, r12 are used to save rcx, rdx, r8, r9, NEVER touch these!
+ // r10 is used for function pointer, NEVER touch this!
+ // so we can ONLY safely use: r11, r13, r14, r15, rax
+
+ if (!settings)
+ {
return "";
}
- if (minInst == -1) {
+
+ if (minInst == -1)
+ {
minInst = settings->value("obfuscation/min_instructions", 2).toInt();
}
- if (maxInst == -1) {
+
+ if (maxInst == -1)
+ {
maxInst = settings->value("obfuscation/max_instructions", 8).toInt();
}
- if (!useAdvanced) {
+
+ if (!useAdvanced)
+ {
useAdvanced = settings->value("obfuscation/use_advanced_junk", false).toBool();
}
- QStringList junkInstructions = {
+ QStringList junkInstructions =
+ {
" nop\n",
" xchg r11, r11\n",
" xchg r13, r13\n",
@@ -51,8 +67,11 @@ QString DirectObfuscation::JunkGenerator::generateJunkInstructions(int minInst,
" xor r14, 0\n",
" xor r15, 0\n"
};
- if (useAdvanced) {
- QStringList advancedJunk = {
+
+ if (useAdvanced)
+ {
+ QStringList advancedJunk =
+ {
" pause\n",
" fnop\n",
" cld\n",
@@ -78,37 +97,51 @@ QString DirectObfuscation::JunkGenerator::generateJunkInstructions(int minInst,
" sfence\n",
" mfence\n"
};
+
int advancedCount = getRandomInt(2, 8);
- for (int i = 0; i < advancedCount; ++i) {
+
+ for (int i = 0; i < advancedCount; ++i)
+ {
junkInstructions.append(advancedJunk[getRandomInt(0, advancedJunk.size() - 1)]);
}
}
+
int numInstructions = getRandomInt(minInst, maxInst);
QString result;
- for (int i = 0; i < numInstructions; ++i) {
+
+ for (int i = 0; i < numInstructions; ++i)
+ {
result += junkInstructions[getRandomInt(0, junkInstructions.size() - 1)];
}
+
return result;
}
-QString DirectObfuscation::JunkGenerator::getRandomJunkInstruction() {
- QStringList instructions = {
+QString DirectObfuscation::JunkGenerator::getRandomJunkInstruction()
+{
+ QStringList instructions =
+ {
" nop\n",
" xchg r8, r8\n",
" test r8, r8\n"
};
+
return instructions[getRandomInt(0, instructions.size() - 1)];
}
-QString DirectObfuscation::JunkGenerator::getRandomAdvancedJunkInstruction() {
- QStringList instructions = {
+QString DirectObfuscation::JunkGenerator::getRandomAdvancedJunkInstruction()
+{
+ QStringList instructions =
+ {
" pause\n",
" fnop\n",
" cld\n"
};
+
return instructions[getRandomInt(0, instructions.size() - 1)];
}
-int DirectObfuscation::JunkGenerator::getRandomInt(int min, int max) {
+int DirectObfuscation::JunkGenerator::getRandomInt(int min, int max)
+{
return QRandomGenerator::global()->bounded(min, max + 1);
}
diff --git a/Bind/src/Core/Obfuscation/Direct/Stub/DirectStubGenerator.cpp b/Bind/src/Core/Obfuscation/Direct/Stub/DirectStubGenerator.cpp
index dcc1c99..65d3833 100644
--- a/Bind/src/Core/Obfuscation/Direct/Stub/DirectStubGenerator.cpp
+++ b/Bind/src/Core/Obfuscation/Direct/Stub/DirectStubGenerator.cpp
@@ -5,41 +5,59 @@
#include
#include
-DirectObfuscation::StubGenerator::StubGenerator(QSettings* settings) : settings(settings) {
-}
+DirectObfuscation::StubGenerator::StubGenerator(QSettings* settings)
+ : settings(settings)
+{}
-void DirectObfuscation::StubGenerator::setSettings(QSettings* settings) {
+void DirectObfuscation::StubGenerator::setSettings(QSettings* settings)
+{
this->settings = settings;
}
-QString DirectObfuscation::StubGenerator::generateMaskedSequence(const QString& offsetName, const QMap& encryptionData, int method) {
- if (!settings) {
+QString DirectObfuscation::StubGenerator::generateMaskedSequence(const QString& offsetName, const QMap& encryptionData, int method)
+{
+ if (!settings)
+ {
return "";
}
+
JunkGenerator junkGen(settings);
Encryptor encryptor(settings);
bool enableEncryption = settings->value("obfuscation/enable_encryption", true).toBool();
- QStringList movR10RcxVariants = {
+
+ QStringList movR10RcxVariants =
+ {
" mov r10, rcx\n",
" lea r10, [rcx]\n",
" mov r11, rcx\n xchg r10, r11\n",
" mov r12, rcx\n mov r10, r12\n",
" mov r13, rcx\n mov r10, r13\n"
};
+
QStringList syscallSequence;
- if (enableEncryption && !encryptionData.isEmpty()) {
+
+ if (enableEncryption && !encryptionData.isEmpty())
+ {
syscallSequence = encryptor.generateDecryptionSequence(offsetName, encryptionData, method);
- } else {
- QStringList movEaxOffsetVariants = {
+ }
+ else
+ {
+ QStringList movEaxOffsetVariants =
+ {
QString(" xor eax, eax\n add eax, dword ptr [%1]\n").arg(offsetName),
QString(" mov ebx, dword ptr [%1]\n xchg eax, ebx\n").arg(offsetName)
};
+
syscallSequence << movEaxOffsetVariants[getRandomInt(0, movEaxOffsetVariants.size() - 1)];
}
- QStringList syscallVariants = {
+
+ QStringList syscallVariants =
+ {
" syscall\n"
};
- QStringList sequence = {
+
+ QStringList sequence =
+ {
movR10RcxVariants[getRandomInt(0, movR10RcxVariants.size() - 1)],
junkGen.generateJunkInstructions(),
syscallSequence.join(""),
@@ -47,17 +65,24 @@ QString DirectObfuscation::StubGenerator::generateMaskedSequence(const QString&
syscallVariants[getRandomInt(0, syscallVariants.size() - 1)],
" ret"
};
+
return sequence.join("");
}
-QString DirectObfuscation::StubGenerator::generateChunkedSequence(const QString& offsetName, const QMap& encryptionData, int method) {
- if (!settings) {
+QString DirectObfuscation::StubGenerator::generateChunkedSequence(const QString& offsetName, const QMap& encryptionData, int method)
+{
+ if (!settings)
+ {
return "";
}
+
bool enableChunking = settings->value("obfuscation/enable_chunking", true).toBool();
- if (!enableChunking) {
+
+ if (!enableChunking)
+ {
return generateMaskedSequence(offsetName, encryptionData, method);
}
+
JunkGenerator junkGen(settings);
Encryptor encryptor(settings);
SharedObfuscation::NameGenerator nameGen(settings);
@@ -66,13 +91,20 @@ QString DirectObfuscation::StubGenerator::generateChunkedSequence(const QString&
QString entryLabel = nameGen.generateRandomLabel();
QString middleLabel = nameGen.generateRandomLabel();
QString exitLabel = nameGen.generateRandomLabel();
+
QStringList syscallSequence;
- if (enableEncryption && !encryptionData.isEmpty()) {
+
+ if (enableEncryption && !encryptionData.isEmpty())
+ {
syscallSequence = encryptor.generateDecryptionSequence(offsetName, encryptionData, method);
- } else {
+ }
+ else
+ {
syscallSequence << QString(" xor eax, eax\n add eax, dword ptr [%1]\n").arg(offsetName);
}
- QStringList chunks = {
+
+ QStringList chunks =
+ {
QString("%1:\n"
" mov r10, rcx\n"
" %2"
@@ -86,29 +118,38 @@ QString DirectObfuscation::StubGenerator::generateChunkedSequence(const QString&
" %2"
" ret\n").arg(exitLabel).arg(junkGen.generateJunkInstructions())
};
+
QString entry = chunks[0];
QStringList rest = chunks.mid(1);
- for (int i = rest.size() - 1; i > 0; --i) {
+
+ for (int i = rest.size() - 1; i > 0; --i)
+ {
int j = getRandomInt(0, i);
rest.swapItemsAt(i, j);
}
+
chunks = QStringList() << entry << rest;
return chunks.join("");
}
-QString DirectObfuscation::StubGenerator::generateAlignPadding() {
+QString DirectObfuscation::StubGenerator::generateAlignPadding()
+{
JunkGenerator junkGen(settings);
SharedObfuscation::NameGenerator nameGen(settings);
int alignSize = QList{4, 8, 16}[getRandomInt(0, 2)];
QStringList padding;
int paddingCount = getRandomInt(1, 3);
- for (int i = 0; i < paddingCount; ++i) {
+
+ for (int i = 0; i < paddingCount; ++i)
+ {
padding << junkGen.generateJunkInstructions();
}
+
padding << QString("ALIGN %1").arg(alignSize);
return padding.join("");
}
-int DirectObfuscation::StubGenerator::getRandomInt(int min, int max) {
+int DirectObfuscation::StubGenerator::getRandomInt(int min, int max)
+{
return QRandomGenerator::global()->bounded(min, max + 1);
}
diff --git a/Bind/src/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.cpp b/Bind/src/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.cpp
index c511f74..673fd22 100644
--- a/Bind/src/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.cpp
+++ b/Bind/src/Core/Obfuscation/Indirect/ControlFlow/IndirectControlFlow.cpp
@@ -4,23 +4,27 @@
#include
IndirectObfuscation::ControlFlow::ControlFlow(QSettings* settings)
- : settings(settings) {}
+ : settings(settings)
+{}
-QString IndirectObfuscation::ControlFlow::generateControlFlowObfuscation() {
+QString IndirectObfuscation::ControlFlow::generateControlFlowObfuscation()
+{
QString method = settings->value("obfuscation/indirect_control_flow_method", "random").toString();
int pattern;
- if (method == "random") {
- pattern = QRandomGenerator::global()->bounded(4);
- } else {
- QMap methodMap;
- methodMap["register"] = 0;
- methodMap["value"] = 1;
- methodMap["flag"] = 2;
- methodMap["mixed"] = 3;
- pattern = methodMap.value(method, 0);
+
+ ControlFlowPattern flowPattern;
+
+ if (method == "random")
+ {
+ int randomValue = QRandomGenerator::global()->bounded(4);
+ flowPattern = static_cast(randomValue);
}
+ else
+ {
+ flowPattern = stringToControlFlowPattern(method);
+ }
+
QStringList controlFlowPatterns = {
- // Pattern 0: Register Based
QString(" ; Opaque Predicate - Register Based\n"
" test r11, r11\n" // r11 is always 0, so test sets ZF=1
" jnz fake_branch_%1\n" // Never taken (ZF=1, so jnz fails)
@@ -32,7 +36,7 @@ QString IndirectObfuscation::ControlFlow::generateControlFlowObfuscation() {
" add r14, 0\n" // Dead code
"real_code_%1:\n")
.arg(QRandomGenerator::global()->bounded(1000, 999999)),
- // Pattern 1: Value Based
+
QString(" ; Opaque Predicate - Value Based\n"
" mov r15, 0\n" // Set r15 to 0
" cmp r15, 1\n" // Compare 0 with 1 (always false)
@@ -45,7 +49,7 @@ QString IndirectObfuscation::ControlFlow::generateControlFlowObfuscation() {
" test r13, r13\n" // Dead code
"real_code_%1:\n")
.arg(QRandomGenerator::global()->bounded(1000, 999999)),
- // Pattern 2: Flag Based
+
QString(" ; Opaque Predicate - Flag Based\n"
" clc\n" // Clear carry flag
" jc fake_branch_%1\n" // Never taken (CF=0)
@@ -57,7 +61,7 @@ QString IndirectObfuscation::ControlFlow::generateControlFlowObfuscation() {
" xchg r14, r14\n" // Dead code
"real_code_%1:\n")
.arg(QRandomGenerator::global()->bounded(1000, 999999)),
- // Pattern 3: Mixed Junk Code
+
QString(" ; Opaque Predicate - Mixed Junk Code\n"
" xor r11, r11\n" // r11 = 0
" or r11, 0\n" // r11 still = 0
@@ -73,5 +77,6 @@ QString IndirectObfuscation::ControlFlow::generateControlFlowObfuscation() {
"real_code_%1:\n")
.arg(QRandomGenerator::global()->bounded(1000, 999999))
};
- return controlFlowPatterns[pattern];
+
+ return controlFlowPatterns[static_cast(flowPattern)];
}
diff --git a/Bind/src/Core/Obfuscation/Indirect/Encryption/IndirectEncryptor.cpp b/Bind/src/Core/Obfuscation/Indirect/Encryption/IndirectEncryptor.cpp
index c328ee5..4bc2f6a 100644
--- a/Bind/src/Core/Obfuscation/Indirect/Encryption/IndirectEncryptor.cpp
+++ b/Bind/src/Core/Obfuscation/Indirect/Encryption/IndirectEncryptor.cpp
@@ -1,12 +1,19 @@
#include "include/Core/Obfuscation/Indirect/Encryption/IndirectEncryptor.h"
#include
-QString IndirectObfuscation::Encryptor::generateEncryptedSyscallNumbers() {
+QString IndirectObfuscation::Encryptor::generateEncryptedSyscallNumbers()
+{
QString encryptedCode;
int encryptionKey = QRandomGenerator::global()->bounded(1, 256);
QString khex = QString::number(encryptionKey, 16).toUpper();
- if (khex.length() < 2) khex.prepend('0');
+
+ if (khex.length() < 2)
+ {
+ khex.prepend('0');
+ }
+
int offset = QRandomGenerator::global()->bounded(8, 32);
+
encryptedCode = QString(" ; Encrypted syscall number handling\n"
" ; Key: 0%1h\n"
" mov rax, [rsp+%2]\n"
@@ -14,5 +21,6 @@ QString IndirectObfuscation::Encryptor::generateEncryptedSyscallNumbers() {
" mov [rsp+%2], rax\n")
.arg(khex)
.arg(offset);
+
return encryptedCode;
}
diff --git a/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectJunkGenerator.cpp b/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectJunkGenerator.cpp
index 1b60cd3..b916432 100644
--- a/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectJunkGenerator.cpp
+++ b/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectJunkGenerator.cpp
@@ -4,13 +4,16 @@
#include
IndirectObfuscation::JunkGenerator::JunkGenerator(QSettings* settings)
- : settings(settings) {}
+ : settings(settings)
+{}
-QString IndirectObfuscation::JunkGenerator::generateRegisterSafeJunk() {
+QString IndirectObfuscation::JunkGenerator::generateRegisterSafeJunk()
+{
// rcx, rdx, r8, r9 are function parameters, NEVER touch these!
// rbx, rsi, rdi, r12 are used to save rcx, rdx, r8, r9, NEVER touch these!
// r10 is used for function pointer, NEVER touch this!
// so we can ONLY safely use: r11, r13, r14, r15, rax
+
QStringList safeJunkInstructions = {
" nop\n",
" xchg r11, r11\n",
@@ -58,16 +61,29 @@ QString IndirectObfuscation::JunkGenerator::generateRegisterSafeJunk() {
" lfence\n",
" mfence\n"
};
- int numInstructions = QRandomGenerator::global()->bounded(1, 4); // Reduced for safety
+
+ int numInstructions = QRandomGenerator::global()->bounded(1, 4);
int minJ = settings->value("obfuscation/indirect_min_instructions", 2).toInt();
int maxJ = settings->value("obfuscation/indirect_max_instructions", 8).toInt();
- if (minJ < 1) minJ = 1;
- if (maxJ < minJ) maxJ = minJ;
+
+ if (minJ < 1)
+ {
+ minJ = 1;
+ }
+
+ if (maxJ < minJ)
+ {
+ maxJ = minJ;
+ }
+
numInstructions = QRandomGenerator::global()->bounded(minJ, maxJ + 1);
+
QString junkCode;
- for (int i = 0; i < numInstructions; ++i) {
+ for (int i = 0; i < numInstructions; ++i)
+ {
int index = QRandomGenerator::global()->bounded(safeJunkInstructions.size());
junkCode += safeJunkInstructions[index];
}
+
return junkCode;
}
diff --git a/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.cpp b/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.cpp
index 72f7438..3fdc4bf 100644
--- a/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.cpp
+++ b/Bind/src/Core/Obfuscation/Indirect/Stub/IndirectStubGenerator.cpp
@@ -4,43 +4,50 @@
#include
IndirectObfuscation::StubGenerator::StubGenerator(QSettings* settings)
- : settings(settings) {}
+ : settings(settings)
+{}
-QString IndirectObfuscation::StubGenerator::obfuscateResolverCall(const QString& originalCall) {
- if (settings->value("obfuscation/indirect_obfuscate_calls", true).toBool()) {
+QString IndirectObfuscation::StubGenerator::obfuscateResolverCall(const QString& originalCall)
+{
+ if (settings->value("obfuscation/indirect_obfuscate_calls", true).toBool())
+ {
QString method = settings->value("obfuscation/indirect_resolver_method", "random").toString();
- if (method == "random") {
- method = QString::number(QRandomGenerator::global()->bounded(4));
+
+ ResolverCallMethod callMethod;
+
+ if (method == "random")
+ {
+ int randomValue = QRandomGenerator::global()->bounded(4);
+ callMethod = static_cast(randomValue);
}
- QMap methodMap;
- methodMap["register"] = 0;
- methodMap["stack"] = 1;
- methodMap["indirect"] = 2;
- methodMap["shuffle"] = 3;
- int pattern = methodMap.value(method, 0);
- switch (pattern) {
- case 0:
- // Pattern 1: Register pointer call via R10
+ else
+ {
+ callMethod = stringToResolverCallMethod(method);
+ }
+
+ switch (callMethod)
+ {
+ case ResolverCallMethod::RegisterPointer:
return " ; RegPtr_R10_Call\n"
" lea r10, [GetSyscallNumber]\n"
" call r10";
- case 1:
- // Pattern 2: Stack indirect call (16 byte aligned)
+
+ case ResolverCallMethod::StackIndirect:
return " ; StackIndirect_Aligned\n"
" sub rsp, 16\n"
" lea rax, [GetSyscallNumber]\n"
" mov [rsp], rax\n"
" call qword ptr [rsp]\n"
" add rsp, 16";
- case 2:
- // Pattern 3: Stack scratch space indirect call
+
+ case ResolverCallMethod::StackScratch:
return " ; StackScratchIndirect\n"
" lea rax, [GetSyscallNumber]\n"
" mov [rsp-8], rax\n"
" lea rax, [rsp-8]\n"
" call qword ptr [rax]";
- case 3:
- // Pattern 4: Register shuffle call via R10
+
+ case ResolverCallMethod::RegisterShuffle:
return " ; RegShuffle_R10_Call\n"
" push r10\n"
" lea r10, [GetSyscallNumber]\n"
@@ -48,5 +55,6 @@ QString IndirectObfuscation::StubGenerator::obfuscateResolverCall(const QString&
" pop r10";
}
}
+
return originalCall;
}
diff --git a/Bind/src/Core/Obfuscation/IndirectObfuscation.cpp b/Bind/src/Core/Obfuscation/IndirectObfuscation.cpp
index a01940d..60fc6ab 100644
--- a/Bind/src/Core/Obfuscation/IndirectObfuscation.cpp
+++ b/Bind/src/Core/Obfuscation/IndirectObfuscation.cpp
@@ -12,94 +12,129 @@
#include
#include
-IndirectObfuscationManager::IndirectObfuscationManager(QSettings* settings)
- : settings(settings), outputCallback(nullptr) {
-}
+IndirectObfuscationManager::IndirectObfuscationManager(QSettings* settings)
+ : settings(settings)
+ , outputCallback(nullptr)
+{}
-void IndirectObfuscationManager::setOutputCallback(std::function callback) {
+void IndirectObfuscationManager::setOutputCallback(std::function callback)
+{
outputCallback = callback;
}
-void IndirectObfuscationManager::logMessage(const QString& message) {
- if (outputCallback) {
+void IndirectObfuscationManager::logMessage(const QString& message)
+{
+ if (outputCallback)
+ {
outputCallback(message);
}
+
qDebug() << "IndirectObfuscation:" << message;
}
-QString IndirectObfuscationManager::getIndirectPrefix() {
+QString IndirectObfuscationManager::getIndirectPrefix()
+{
return "SysIndirect";
}
-bool IndirectObfuscationManager::isIndirectMode() {
+bool IndirectObfuscationManager::isIndirectMode()
+{
return settings->value("general/indirect_assembly", false).toBool();
}
-bool IndirectObfuscationManager::generateIndirectObfuscation() {
+bool IndirectObfuscationManager::generateIndirectObfuscation()
+{
logMessage("Starting Indirect Obfuscation...");
+
bool isKernel = settings->value("general/syscall_mode", "Nt").toString() == "Zw";
- QString asmPath = isKernel ?
- PathUtils::getSysCallerKPath() + "/Wrapper/src/syscaller.asm" :
- PathUtils::getSysCallerPath() + "/Wrapper/src/syscaller.asm";
+
+ QString asmPath = isKernel ?
+ PathUtils::getSysCallerKPath() + "/Wrapper/src/syscaller.asm" :
+ PathUtils::getSysCallerPath() + "/Wrapper/src/syscaller.asm";
+
QString headerPath = isKernel ?
- PathUtils::getSysCallerKPath() + "/Wrapper/include/SysK/sysFunctions_k.h" :
- PathUtils::getSysCallerPath() + "/Wrapper/include/Sys/sysFunctions.h";
+ PathUtils::getSysCallerKPath() + "/Wrapper/include/SysK/sysFunctions_k.h" :
+ PathUtils::getSysCallerPath() + "/Wrapper/include/Sys/sysFunctions.h";
+
return processIndirectAssemblyFile(asmPath, headerPath);
}
-bool IndirectObfuscationManager::processIndirectAssemblyFile(const QString& asmPath, const QString& headerPath) {
+bool IndirectObfuscationManager::processIndirectAssemblyFile(const QString& asmPath, const QString& headerPath)
+{
QFile asmFile(asmPath);
- if (!asmFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!asmFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
logMessage("Failed to open Assembly File: " + asmPath);
return false;
}
+
QTextStream in(&asmFile);
QStringList content = in.readAll().split('\n');
asmFile.close();
+
QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList();
bool useAllSyscalls = selectedSyscalls.isEmpty();
QString indirectPrefix = getIndirectPrefix();
QMap indirectStubs;
QSet usedNames;
QMap syscallMap; // original -> obfuscated
+
QStringList currentStub;
QString currentSyscall;
bool inStub = false;
- for (const QString& line : content) {
+ for (const QString& line : content)
+ {
QRegularExpression procRegex(QString("(%1\\w+)\\s+PROC").arg(indirectPrefix));
QRegularExpressionMatch procMatch = procRegex.match(line);
- if (procMatch.hasMatch()) {
+
+ if (procMatch.hasMatch())
+ {
currentSyscall = procMatch.captured(1);
inStub = true;
currentStub.clear();
currentStub << line;
- if (useAllSyscalls || selectedSyscalls.contains(currentSyscall)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentSyscall))
+ {
indirectStubs[currentSyscall] = currentStub;
}
- } else if (inStub) {
+ }
+ else if (inStub)
+ {
currentStub << line;
- if (line.contains(" ENDP")) {
+
+ if (line.contains(" ENDP"))
+ {
inStub = false;
- if (useAllSyscalls || selectedSyscalls.contains(currentSyscall)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentSyscall))
+ {
indirectStubs[currentSyscall] = currentStub;
}
}
}
}
- if (!indirectStubs.isEmpty()) {
+ if (!indirectStubs.isEmpty())
+ {
SharedObfuscation::NameGenerator nameGen(settings);
int indirectPrefixLength = settings->value("obfuscation/indirect_syscall_prefix_length",
- settings->value("obfuscation/syscall_prefix_length", 8).toInt()).toInt();
+ settings->value("obfuscation/syscall_prefix_length", 8).toInt()).toInt();
int indirectNumberLength = settings->value("obfuscation/indirect_syscall_number_length",
- settings->value("obfuscation/syscall_number_length", 6).toInt()).toInt();
- for (auto it = indirectStubs.begin(); it != indirectStubs.end(); ++it) {
+ settings->value("obfuscation/syscall_number_length", 6).toInt()).toInt();
+
+ for (auto it = indirectStubs.begin(); it != indirectStubs.end(); ++it)
+ {
const QString& original = it.key();
- if (!syscallMap.contains(original)) {
+
+ if (!syscallMap.contains(original))
+ {
syscallMap[original] = nameGen.generateRandomName(usedNames, indirectPrefixLength, indirectNumberLength);
}
}
}
- for (auto it = indirectStubs.begin(); it != indirectStubs.end(); ++it) {
+ for (auto it = indirectStubs.begin(); it != indirectStubs.end(); ++it)
+ {
QStringList obfuscatedStub;
bool inProcBlock = false;
bool pendingEncString = false;
@@ -107,14 +142,20 @@ bool IndirectObfuscationManager::processIndirectAssemblyFile(const QString& asmP
int pendingPlainLen = 0;
quint8 pendingKey = 0;
bool encAdjustActive = false; // when true, convert next add rsp,32 to add rsp,64
- for (const QString& line : it.value()) {
+
+ for (const QString& line : it.value())
+ {
QString obfuscatedLine = line;
- if (line.contains(" PROC")) {
+
+ if (line.contains(" PROC"))
+ {
inProcBlock = true;
obfuscatedStub << line;
continue;
}
- if (line.contains(" ENDP")) {
+
+ if (line.contains(" ENDP"))
+ {
inProcBlock = false;
pendingEncString = false;
pendingEncBytes.clear();
@@ -122,70 +163,109 @@ bool IndirectObfuscationManager::processIndirectAssemblyFile(const QString& asmP
obfuscatedStub << line;
continue;
}
- if (inProcBlock) {
- if (settings->value("obfuscation/indirect_enable_junk", true).toBool()) {
+
+ if (inProcBlock)
+ {
+ if (settings->value("obfuscation/indirect_enable_junk", true).toBool())
+ {
IndirectObfuscation::JunkGenerator JunkGenerator(settings);
QString junkCode = JunkGenerator.generateRegisterSafeJunk();
- if (!junkCode.isEmpty()) {
+
+ if (!junkCode.isEmpty())
+ {
QStringList junkLines = junkCode.split('\n');
- for (const QString& junkLine : junkLines) {
- if (!junkLine.trimmed().isEmpty()) {
+
+ for (const QString& junkLine : junkLines)
+ {
+ if (!junkLine.trimmed().isEmpty())
+ {
obfuscatedStub << " " + junkLine.trimmed();
}
}
}
}
- if (settings->value("obfuscation/indirect_encrypt_strings", false).toBool()) {
- QRegularExpression strRx(R"(^\s*lea\s+rcx,\s*\[(\w+)_str\]\s*$)", QRegularExpression::CaseInsensitiveOption);
+
+ if (settings->value("obfuscation/indirect_encrypt_strings", false).toBool())
+ {
+ QRegularExpression strRx(R"(^\s*lea\s+rcx,\s*\[(\w+)_str\]\s*$)",
+ QRegularExpression::CaseInsensitiveOption);
auto m = strRx.match(line);
- if (m.hasMatch()) {
+
+ if (m.hasMatch())
+ {
QString label = m.captured(1);
QByteArray plain = label.toUtf8();
plain.append('\0');
- if (plain.size() <= 32) {
+
+ if (plain.size() <= 32)
+ {
pendingKey = static_cast(QRandomGenerator::global()->bounded(1, 256));
pendingEncBytes = QByteArray(plain);
- for (int i = 0; i < pendingEncBytes.size(); ++i) pendingEncBytes[i] = pendingEncBytes[i] ^ pendingKey;
+
+ for (int i = 0; i < pendingEncBytes.size(); ++i)
+ pendingEncBytes[i] = pendingEncBytes[i] ^ pendingKey;
+
pendingPlainLen = plain.size();
pendingEncString = true;
continue;
}
}
}
- // if we have pending enc string and see shadow space reservation, emit the build+decrypt into shadow space
- if (pendingEncString && line.trimmed().startsWith("sub rsp, 32")) {
+
+ // if we have pending enc string and see shadow space reservation,
+ // emit the build+decrypt into shadow space
+ if (pendingEncString && line.trimmed().startsWith("sub rsp, 32"))
+ {
// replace with sub rsp, 64 to allocate extra 32 bytes (shadow + our buffer)
obfuscatedStub << " sub rsp, 64";
encAdjustActive = true;
- // now emit write+decrypt sequence using only rax, rcx, r11, r8b; buffer base is [rsp+20h]
+
+ // now emit write+decrypt sequence using only rax, rcx, r11, r8b;
+ // buffer base is [rsp+20h]
obfuscatedStub << " ; Build decrypted resolver string in shadow space";
int lblId = QRandomGenerator::global()->bounded(1000, 999999);
QString loopLbl = QString("dec_loop_cf_%1").arg(lblId);
QString doneLbl = QString("dec_done_cf_%1").arg(lblId);
+
// write encrypted qwords into [rsp+off]
- for (int off = 0; off < 32; off += 8) {
+ for (int off = 0; off < 32; off += 8)
+ {
quint64 q = 0;
- for (int b = 0; b < 8; ++b) {
+
+ for (int b = 0; b < 8; ++b)
+ {
int idx = off + b;
unsigned char val = 0;
- if (idx < pendingEncBytes.size()) val = static_cast(pendingEncBytes[idx]);
+
+ if (idx < pendingEncBytes.size())
+ val = static_cast(pendingEncBytes[idx]);
+
q |= (static_cast(val) << (8 * b));
}
+
QString hex = QString::number(static_cast(q), 16).toUpper();
while (hex.length() < 16) hex.prepend('0');
+
obfuscatedStub << QString(" mov rax, 0%1h").arg(hex);
- if (off == 0) {
+
+ if (off == 0)
+ {
obfuscatedStub << " mov qword ptr [rsp+20h], rax";
- } else {
+ }
+ else
+ {
obfuscatedStub << QString(" mov qword ptr [rsp+20h+%1], rax").arg(off);
}
}
+
obfuscatedStub << QString(" mov r11d, %1").arg(pendingPlainLen);
+
{
QString khex = QString::number(pendingKey, 16).toUpper();
if (khex.length() < 2) khex.prepend('0');
obfuscatedStub << QString(" mov al, 0%1h").arg(khex);
}
+
obfuscatedStub << " ; decrypt in place: for i in [0..len) shadow[i] ^= al";
obfuscatedStub << " xor rcx, rcx";
obfuscatedStub << loopLbl + ":";
@@ -197,132 +277,199 @@ bool IndirectObfuscationManager::processIndirectAssemblyFile(const QString& asmP
obfuscatedStub << " inc rcx";
obfuscatedStub << " jmp " + loopLbl;
obfuscatedStub << doneLbl + ":";
+
obfuscatedStub << " lea rcx, [rsp+20h]"; // rcx = decrypted buffer out of callee home space
+
pendingEncString = false;
pendingEncBytes.clear();
pendingPlainLen = 0;
continue;
}
- if (encAdjustActive && line.trimmed().startsWith("add rsp, 32")) {
+
+ if (encAdjustActive && line.trimmed().startsWith("add rsp, 32"))
+ {
obfuscatedStub << " add rsp, 64";
encAdjustActive = false;
continue;
}
- if (line.contains("call GetSyscallNumber")) {
+
+ if (line.contains("call GetSyscallNumber"))
+ {
IndirectObfuscation::StubGenerator stub(settings);
obfuscatedLine = stub.obfuscateResolverCall(line);
}
- if (settings->value("obfuscation/indirect_enable_control_flow", false).toBool()) {
+
+ if (settings->value("obfuscation/indirect_enable_control_flow", false).toBool())
+ {
IndirectObfuscation::ControlFlow cf(settings);
QString controlFlowCode = cf.generateControlFlowObfuscation();
- if (!controlFlowCode.isEmpty()) {
+
+ if (!controlFlowCode.isEmpty())
+ {
QStringList controlFlowLines = controlFlowCode.split('\n');
- for (const QString& flowLine : controlFlowLines) {
- if (!flowLine.trimmed().isEmpty()) {
+
+ for (const QString& flowLine : controlFlowLines)
+ {
+ if (!flowLine.trimmed().isEmpty())
+ {
obfuscatedStub << " " + flowLine.trimmed();
}
}
}
}
}
+
obfuscatedStub << obfuscatedLine;
}
QStringList renamedStub;
- for (const QString& sLine : obfuscatedStub) {
+
+ for (const QString& sLine : obfuscatedStub)
+ {
QString newLine = sLine;
QRegularExpression nameRx(QString("((%1\\w+))\\s+(PROC|ENDP)").arg(indirectPrefix));
auto m = nameRx.match(newLine);
- if (m.hasMatch()) {
+
+ if (m.hasMatch())
+ {
QString originalName = m.captured(1);
- if (syscallMap.contains(originalName)) {
+
+ if (syscallMap.contains(originalName))
+ {
newLine = newLine.replace(originalName, syscallMap.value(originalName));
}
}
+
renamedStub << newLine;
}
+
it.value() = renamedStub;
}
QFile outAsmFile(asmPath);
- if (!outAsmFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (!outAsmFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
logMessage("Failed to write Assembly File: " + asmPath);
return false;
}
+
QTextStream out(&outAsmFile);
bool inProcessedStub = false;
bool injectedAliases = false;
QString currentStubName;
- for (int i = 0; i < content.size(); ++i) {
+
+ for (int i = 0; i < content.size(); ++i)
+ {
const QString& line = content[i];
- if (!injectedAliases && line.trimmed().compare(".code", Qt::CaseInsensitive) == 0) {
+
+ if (!injectedAliases && line.trimmed().compare(".code", Qt::CaseInsensitive) == 0)
+ {
out << line << "\n\n";
- if (!syscallMap.isEmpty()) {
+
+ if (!syscallMap.isEmpty())
+ {
out << "; Public Declarations\n";
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
out << QString("PUBLIC %1\n").arg(it.value());
}
+
out << "\n; Export Aliases\n";
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
out << QString("ALIAS <%1> = <%2>\n").arg(it.key()).arg(it.value());
}
+
out << "\n";
}
+
injectedAliases = true;
continue;
}
+
QRegularExpression procRegex(QString("(%1\\w+)\\s+PROC").arg(indirectPrefix));
QRegularExpressionMatch procMatch = procRegex.match(line);
- if (procMatch.hasMatch()) {
+
+ if (procMatch.hasMatch())
+ {
QString stubName = procMatch.captured(1);
- if (indirectStubs.contains(stubName)) {
+
+ if (indirectStubs.contains(stubName))
+ {
inProcessedStub = true;
currentStubName = stubName;
- for (const QString& stubLine : indirectStubs[stubName]) {
+
+ for (const QString& stubLine : indirectStubs[stubName])
+ {
out << stubLine << "\n";
}
+
continue;
}
}
- if (inProcessedStub && line.contains(" ENDP")) {
+
+ if (inProcessedStub && line.contains(" ENDP"))
+ {
inProcessedStub = false;
currentStubName.clear();
continue;
}
- if (inProcessedStub) {
+
+ if (inProcessedStub)
+ {
continue;
}
+
out << line << "\n";
}
outAsmFile.close();
- if (!updateIndirectHeaderFile(headerPath, syscallMap)) {
+
+ if (!updateIndirectHeaderFile(headerPath, syscallMap))
+ {
logMessage("Failed to update Header File for indirect obfuscation");
return false;
}
+
bool bindingsEnabled = settings->value("general/bindings_enabled", false).toBool();
bool isKernel = settings->value("general/syscall_mode", "Nt").toString() == "Zw";
- if (bindingsEnabled && !isKernel) {
+
+ if (bindingsEnabled && !isKernel)
+ {
QString defPath = PathUtils::getSysCallerPath() + "/Wrapper/SysCaller.def";
QStringList obfuscatedNames;
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
obfuscatedNames << it.value();
}
- if (!updateDefFile(defPath, obfuscatedNames)) {
+
+ if (!updateDefFile(defPath, obfuscatedNames))
+ {
logMessage("Failed to update DEF File for indirect obfuscation");
return false;
}
}
+
logMessage("Indirect Obfuscation completed successfully!");
return true;
}
-bool IndirectObfuscationManager::updateIndirectHeaderFile(const QString& headerPath, const QMap& syscallMap) {
+bool IndirectObfuscationManager::updateIndirectHeaderFile(const QString& headerPath,
+ const QMap& syscallMap)
+{
QFile headerFile(headerPath);
- if (!headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!headerFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
logMessage("Failed to open Header File: " + headerPath);
return false;
}
+
QTextStream in(&headerFile);
QStringList headerContent = in.readAll().split('\n');
headerFile.close();
+
QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList();
bool useAllSyscalls = selectedSyscalls.isEmpty();
QString indirectPrefix = getIndirectPrefix();
@@ -330,99 +477,145 @@ bool IndirectObfuscationManager::updateIndirectHeaderFile(const QString& headerP
bool headerPartEnded = false;
bool skipBlock = false;
QString currentFunc;
- for (const QString& line : headerContent) {
+
+ for (const QString& line : headerContent)
+ {
if (!headerPartEnded && (
line.contains(QString("NTSTATUS %1").arg(indirectPrefix)) ||
line.contains(QString("ULONG %1").arg(indirectPrefix)) ||
line.contains(QString("BOOLEAN %1").arg(indirectPrefix)) ||
line.contains(QString("VOID %1").arg(indirectPrefix)) ||
line.contains("#ifdef __cplusplus")
- )) {
+ ))
+ {
headerPartEnded = true;
}
- if (!headerPartEnded) {
+
+ if (!headerPartEnded)
+ {
newHeaderContent << line;
continue;
}
+
// preserve c++ guards and extern blocks
if (line.contains("#ifdef __cplusplus") || line.contains("extern \"C\"") ||
- line.trimmed() == "{" || line.trimmed() == "}" || line.contains("#endif")) {
+ line.trimmed() == "{" || line.trimmed() == "}" || line.contains("#endif"))
+ {
newHeaderContent << line;
continue;
}
- if (line.contains(QString("%1").arg(indirectPrefix))) {
- QRegularExpression regex(QString(R"((?:extern\s+\"C\"\s+)?(?:NTSTATUS|ULONG|BOOLEAN|VOID) ((?:%1)\w+)\()").arg(indirectPrefix));
+
+ if (line.contains(QString("%1").arg(indirectPrefix)))
+ {
+ QRegularExpression regex(QString(R"((?:extern\s+\"C\"\s+)?(?:NTSTATUS|ULONG|BOOLEAN|VOID) ((?:%1)\w+)\()")
+ .arg(indirectPrefix));
auto m = regex.match(line);
- if (m.hasMatch()) {
+
+ if (m.hasMatch())
+ {
QString originalName = m.captured(1);
currentFunc = originalName;
- if (useAllSyscalls || selectedSyscalls.contains(currentFunc)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentFunc))
+ {
skipBlock = false;
- if (syscallMap.contains(originalName)) {
+
+ if (syscallMap.contains(originalName))
+ {
QString newLine = line;
QString obf = syscallMap.value(originalName);
newLine = newLine.replace(originalName, obf);
newLine = newLine.replace("extern \"C\" ", "");
newHeaderContent << newLine;
}
- } else {
+ }
+ else
+ {
skipBlock = true;
}
+
continue;
}
}
- if (!skipBlock) {
+
+ if (!skipBlock)
+ {
newHeaderContent << line;
- } else if (line.trimmed() == ");") {
+ }
+ else if (line.trimmed() == ");")
+ {
skipBlock = false;
}
}
newHeaderContent << "";
newHeaderContent << "// Syscall Name Mappings (Indirect)";
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
newHeaderContent << QString("#define %1 %2").arg(it.key()).arg(it.value());
}
+
QStringList cleaned;
bool prevEmpty = false;
- for (const QString& l : newHeaderContent) {
- if (l.trimmed().isEmpty()) {
- if (!prevEmpty) {
+
+ for (const QString& l : newHeaderContent)
+ {
+ if (l.trimmed().isEmpty())
+ {
+ if (!prevEmpty)
+ {
cleaned << l;
prevEmpty = true;
}
- } else {
+ }
+ else
+ {
cleaned << l;
prevEmpty = false;
}
}
+
QFile outHeaderFile(headerPath);
- if (!outHeaderFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (!outHeaderFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
logMessage("Failed to write Header File: " + headerPath);
return false;
}
+
QTextStream hout(&outHeaderFile);
hout << cleaned.join("\n");
outHeaderFile.close();
return true;
}
-bool IndirectObfuscationManager::updateDefFile(const QString& defPath, const QStringList& obfuscatedNames) {
+bool IndirectObfuscationManager::updateDefFile(const QString& defPath,
+ const QStringList& obfuscatedNames)
+{
QFile defFile(defPath);
- if (!defFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (!defFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
logMessage("Failed to write DEF File: " + defPath);
return false;
}
+
QTextStream out(&defFile);
out << "LIBRARY SysCaller\n";
out << "EXPORTS\n";
- for (const QString& name : obfuscatedNames) {
+
+ for (const QString& name : obfuscatedNames)
+ {
out << " " << name << "\n";
}
- if (settings && settings->value("general/indirect_assembly", false).toBool()) {
+
+ if (settings && settings->value("general/indirect_assembly", false).toBool())
+ {
out << " GetSyscallNumber\n";
out << " InitializeResolver\n";
out << " CleanupResolver\n";
}
+
defFile.close();
return true;
}
diff --git a/Bind/src/Core/Obfuscation/Obfuscation.cpp b/Bind/src/Core/Obfuscation/Obfuscation.cpp
index 8b59bab..5481a68 100644
--- a/Bind/src/Core/Obfuscation/Obfuscation.cpp
+++ b/Bind/src/Core/Obfuscation/Obfuscation.cpp
@@ -15,79 +15,116 @@
#include
#include
-Obfuscation::Obfuscation() : outputCallback(nullptr), settings(nullptr) {
+Obfuscation::Obfuscation()
+ : outputCallback(nullptr)
+ , settings(nullptr)
+{
settings = new QSettings(PathUtils::getIniPath(), QSettings::IniFormat);
}
-void Obfuscation::setOutputCallback(std::function callback) {
+void Obfuscation::setOutputCallback(std::function callback)
+{
outputCallback = callback;
}
-void Obfuscation::logMessage(const QString& message) {
- if (outputCallback) {
+void Obfuscation::logMessage(const QString& message)
+{
+ if (outputCallback)
+ {
outputCallback(message);
}
+
qDebug() << "Obfuscation:" << message;
}
-int Obfuscation::extractSyscallOffset(const QString& line) {
+int Obfuscation::extractSyscallOffset(const QString& line)
+{
QRegularExpression regex(R"(mov eax,\s*([0-9A-Fa-f]+)h)");
QRegularExpressionMatch match = regex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString offsetStr = match.captured(1);
bool ok;
int offset = offsetStr.toInt(&ok, 16);
- if (ok) {
+
+ if (ok)
+ {
return offset;
}
}
+
return -1;
}
-QString Obfuscation::getAsmFilePath(bool isKernelMode) {
- if (isKernelMode) {
+QString Obfuscation::getAsmFilePath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return PathUtils::getSysCallerKPath() + "/Wrapper/src/syscaller.asm";
- } else {
+ }
+ else
+ {
return PathUtils::getSysCallerPath() + "/Wrapper/src/syscaller.asm";
}
}
-QString Obfuscation::getHeaderFilePath(bool isKernelMode) {
- if (isKernelMode) {
+QString Obfuscation::getHeaderFilePath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return PathUtils::getSysCallerKPath() + "/Wrapper/include/SysK/sysFunctions_k.h";
- } else {
+ }
+ else
+ {
return PathUtils::getSysCallerPath() + "/Wrapper/include/Sys/sysFunctions.h";
}
}
-QString Obfuscation::getDefFilePath(bool isKernelMode) {
- if (isKernelMode) {
+QString Obfuscation::getDefFilePath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return PathUtils::getSysCallerKPath() + "/Wrapper/SysCallerK.def";
- } else {
+ }
+ else
+ {
return PathUtils::getSysCallerPath() + "/Wrapper/SysCaller.def";
}
}
-bool Obfuscation::isKernelMode() {
+bool Obfuscation::isKernelMode()
+{
return settings->value("general/syscall_mode", "Nt").toString() == "Zw";
}
-QString Obfuscation::getSyscallPrefix() {
+QString Obfuscation::getSyscallPrefix()
+{
return isKernelMode() ? "SysK" : "Sys";
}
-int Obfuscation::run(const QStringList& dllPaths) {
- try {
+int Obfuscation::run(const QStringList& dllPaths)
+{
+ try
+ {
bool isIndirectMode = settings->value("general/indirect_assembly", false).toBool();
- if (isIndirectMode) {
+
+ if (isIndirectMode)
+ {
logMessage(Colors::OKBLUE() + "Using Indirect Obfuscation..." + Colors::ENDC());
+
IndirectObfuscationManager indirectObf(settings);
indirectObf.setOutputCallback(outputCallback);
+
bool success = indirectObf.generateIndirectObfuscation();
- if (success) {
+
+ if (success)
+ {
logMessage(Colors::OKGREEN() + "Indirect Obfuscation Completed!" + Colors::ENDC());
return 0;
- } else {
+ }
+ else
+ {
logMessage(Colors::FAIL() + "Indirect Obfuscation Failed!" + Colors::ENDC());
return 1;
}
@@ -95,30 +132,47 @@ int Obfuscation::run(const QStringList& dllPaths) {
QMap syscallSettings = settings->value("stub_mapper/syscall_settings", QMap()).toMap();
bool forceNormal = settings->value("obfuscation/force_normal", false).toBool();
bool forceStubMapper = settings->value("obfuscation/force_stub_mapper", false).toBool();
- if (forceStubMapper || (syscallSettings.size() > 0 && !forceNormal)) {
- logMessage(Colors::OKBLUE() + "Using Stub Mapper..." + Colors::ENDC());
- DirectObfuscation::StubMapper stubMapper(settings);
- stubMapper.setOutputCallback(outputCallback);
- bool success = stubMapper.generateCustomExports();
- if (success) {
- logMessage(Colors::OKGREEN() + "Stub Mapper Obfuscation Completed!" + Colors::ENDC());
- return 0;
- } else {
- logMessage(Colors::FAIL() + "Stub Mapper Obfuscation Failed!" + Colors::ENDC());
- return 1;
- }
- } else {
- logMessage(Colors::OKBLUE() + "Using Normal Obfuscation..." + Colors::ENDC());
- bool success = generateExports();
- if (success) {
- logMessage(Colors::OKGREEN() + "Normal Obfuscation Completed!" + Colors::ENDC());
- return 0;
- } else {
- logMessage(Colors::FAIL() + "Normal Obfuscation Failed!" + Colors::ENDC());
- return 1;
- }
- }
- } catch (const std::exception& e) {
+
+ if (forceStubMapper || (syscallSettings.size() > 0 && !forceNormal))
+ {
+ logMessage(Colors::OKBLUE() + "Using Stub Mapper..." + Colors::ENDC());
+
+ DirectObfuscation::StubMapper stubMapper(settings);
+ stubMapper.setOutputCallback(outputCallback);
+
+ bool success = stubMapper.generateCustomExports();
+
+ if (success)
+ {
+ logMessage(Colors::OKGREEN() + "Stub Mapper Obfuscation Completed!" + Colors::ENDC());
+ return 0;
+ }
+ else
+ {
+ logMessage(Colors::FAIL() + "Stub Mapper Obfuscation Failed!" + Colors::ENDC());
+ return 1;
+ }
+ }
+ else
+ {
+ logMessage(Colors::OKBLUE() + "Using Normal Obfuscation..." + Colors::ENDC());
+
+ bool success = generateExports();
+
+ if (success)
+ {
+ logMessage(Colors::OKGREEN() + "Normal Obfuscation Completed!" + Colors::ENDC());
+ return 0;
+ }
+ else
+ {
+ logMessage(Colors::FAIL() + "Normal Obfuscation Failed!" + Colors::ENDC());
+ return 1;
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
logMessage(Colors::FAIL() + QString("Obfuscation Error: %1").arg(e.what()) + Colors::ENDC());
return 1;
}
@@ -134,15 +188,20 @@ bool Obfuscation::generateExports() {
return processAssemblyFile(asmPath, headerPath);
}
-bool Obfuscation::processAssemblyFile(const QString& asmPath, const QString& headerPath) {
+bool Obfuscation::processAssemblyFile(const QString& asmPath, const QString& headerPath)
+{
QFile asmFile(asmPath);
- if (!asmFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!asmFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to open Assembly File: %1").arg(asmPath) + Colors::ENDC());
return false;
}
+
QTextStream in(&asmFile);
QStringList content = in.readAll().split('\n');
asmFile.close();
+
QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList();
bool useAllSyscalls = selectedSyscalls.isEmpty();
QString syscallPrefix = getSyscallPrefix();
@@ -157,218 +216,336 @@ bool Obfuscation::processAssemblyFile(const QString& asmPath, const QString& hea
QStringList currentStub;
QString currentSyscall;
bool inStub = false;
- for (const QString& line : content) {
+
+ for (const QString& line : content)
+ {
QRegularExpression procRegex(R"((SC\w+|Sys\w+|SysK\w+)\s+PROC)");
QRegularExpressionMatch procMatch = procRegex.match(line);
- if (procMatch.hasMatch()) {
+
+ if (procMatch.hasMatch())
+ {
currentSyscall = procMatch.captured(1);
- if (currentSyscall.startsWith("SC")) {
+
+ if (currentSyscall.startsWith("SC"))
+ {
currentSyscall = syscallPrefix + currentSyscall.mid(2);
}
+
inStub = true;
currentStub.clear();
currentStub << line;
- if (useAllSyscalls || selectedSyscalls.contains(currentSyscall)) {
- if (!syscallMap.contains(currentSyscall)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentSyscall))
+ {
+ if (!syscallMap.contains(currentSyscall))
+ {
SharedObfuscation::NameGenerator nameGen(settings);
syscallMap[currentSyscall] = nameGen.generateRandomName(usedNames);
}
}
- } else if (inStub) {
+ }
+ else if (inStub)
+ {
currentStub << line;
- if (line.contains("mov eax,") && !currentSyscall.isEmpty()) {
+
+ if (line.contains("mov eax,") && !currentSyscall.isEmpty())
+ {
int realOffset = extractSyscallOffset(line);
- if (realOffset != -1) {
+
+ if (realOffset != -1)
+ {
syscallOffsets[currentSyscall] = realOffset;
- if (!realToFakeOffset.contains(realOffset)) {
+
+ if (!realToFakeOffset.contains(realOffset))
+ {
SharedObfuscation::NameGenerator nameGen(settings);
realToFakeOffset[realOffset] = nameGen.generateRandomOffset(usedOffsets);
}
}
- } else if (line.contains(" ENDP")) {
+ }
+ else if (line.contains(" ENDP"))
+ {
inStub = false;
- if (useAllSyscalls || selectedSyscalls.contains(currentSyscall)) {
+
+ if (useAllSyscalls || selectedSyscalls.contains(currentSyscall))
+ {
syscallStubs.append(qMakePair(currentSyscall, currentStub));
}
}
}
}
bool shuffleSequence = settings->value("obfuscation/shuffle_sequence", true).toBool();
- if (shuffleSequence) {
- for (int i = syscallStubs.size() - 1; i > 0; --i) {
+
+ if (shuffleSequence)
+ {
+ for (int i = syscallStubs.size() - 1; i > 0; --i)
+ {
int j = QRandomGenerator::global()->bounded(0, i + 1);
syscallStubs.swapItemsAt(i, j);
}
+
logMessage(Colors::OKGREEN() + "Syscall Sequence has been Randomized" + Colors::ENDC());
}
+
QStringList publics;
QStringList aliases;
bool enableControlFlow = settings->value("obfuscation/control_flow_enabled", false).toBool();
QMap functionSuffixes; // store suffixes for each function
- if (enableControlFlow) {
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ if (enableControlFlow)
+ {
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
QString suffix = QString::number(QRandomGenerator::global()->bounded(1000, 999999));
functionSuffixes[it.key()] = suffix;
}
}
- for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it) {
+
+ for (auto it = syscallMap.begin(); it != syscallMap.end(); ++it)
+ {
QString obfuscatedName = it.value();
- if (enableControlFlow && functionSuffixes.contains(it.key())) {
+
+ if (enableControlFlow && functionSuffixes.contains(it.key()))
+ {
obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffixes[it.key()]);
}
+
publics << QString("PUBLIC %1").arg(obfuscatedName);
aliases << QString("ALIAS <%1> = <%2>").arg(it.key()).arg(obfuscatedName);
}
+
QStringList newContent;
newContent << ".data";
newContent << "ALIGN 8";
+
bool enableEncryption = settings->value("obfuscation/enable_encryption", true).toBool();
- int encryptionMethod = settings->value("obfuscation/encryption_method", 1).toInt();
+ DirectObfuscation::EncryptionMethod encryptionMethod = static_cast(settings->value("obfuscation/encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt());
QMap> encryptionDataMap;
DirectObfuscation::Encryptor encryptor(settings);
SharedObfuscation::NameGenerator nameGen(settings);
- for (auto it = realToFakeOffset.begin(); it != realToFakeOffset.end(); ++it) {
+
+ for (auto it = realToFakeOffset.begin(); it != realToFakeOffset.end(); ++it)
+ {
int realOffset = it.key();
int fakeOffset = it.value();
QString offsetName = nameGen.generateRandomOffsetName(usedOffsetNames);
offsetNameMap[fakeOffset] = offsetName;
- if (enableEncryption) {
- auto encryptionResult = encryptor.encryptOffset(realOffset, encryptionMethod);
+
+ if (enableEncryption)
+ {
+ auto encryptionResult = encryptor.encryptOffset(realOffset, static_cast(encryptionMethod));
int encryptedOffset = encryptionResult.first;
encryptionDataMap[offsetName] = encryptionResult.second;
newContent << QString("%1 dd 0%2h ; Encrypted Syscall ID (Method %3)")
- .arg(offsetName).arg(encryptedOffset, 0, 16).arg(encryptionMethod);
- } else {
+ .arg(offsetName).arg(encryptedOffset, 0, 16).arg(static_cast(encryptionMethod));
+ }
+ else
+ {
newContent << QString("%1 dd 0%2h").arg(offsetName).arg(realOffset, 0, 16);
}
}
newContent << ".code";
newContent << "";
newContent << "; Public Declarations";
- for (const QString& pub : publics) {
+
+ for (const QString& pub : publics)
+ {
newContent << pub;
}
+
newContent << "";
newContent << "; Export Aliases";
- for (const QString& alias : aliases) {
+
+ for (const QString& alias : aliases)
+ {
newContent << alias;
}
+
newContent << "";
+
bool enableInterleaved = settings->value("obfuscation/enable_interleaved", true).toBool();
DirectObfuscation::StubGenerator stubGen(settings);
DirectObfuscation::ControlFlow controlFlow(settings);
- for (const auto& stubPair : syscallStubs) {
+
+ for (const auto& stubPair : syscallStubs)
+ {
QString originalSyscall = stubPair.first;
QStringList stubLines = stubPair.second;
bool skipRest = false; // flag to skip lines after mov eax
QString functionSuffix; // store the random suffix for this function
- if (enableControlFlow && functionSuffixes.contains(originalSyscall)) {
+
+ if (enableControlFlow && functionSuffixes.contains(originalSyscall))
+ {
functionSuffix = functionSuffixes[originalSyscall];
}
- if (enableControlFlow) {
+
+ if (enableControlFlow)
+ {
QString labelPrefix;
- if (syscallMap.contains(originalSyscall)) {
+
+ if (syscallMap.contains(originalSyscall))
+ {
labelPrefix = QString("%1_").arg(syscallMap.value(originalSyscall));
- } else {
+ }
+ else
+ {
labelPrefix = QString("%1_").arg(originalSyscall);
}
+
stubLines = controlFlow.wrapWithControlFlow(stubLines, labelPrefix);
}
- if (enableInterleaved) {
+
+ if (enableInterleaved)
+ {
newContent << stubGen.generateAlignPadding();
}
- for (const QString& originalLine : stubLines) {
- if (skipRest) {
+
+ for (const QString& originalLine : stubLines)
+ {
+ if (skipRest)
+ {
// only process ENDP line when skipping
- if (originalLine.contains(" ENDP")) {
+ if (originalLine.contains(" ENDP"))
+ {
QString line = originalLine;
QRegularExpression syscallRegex(R"((SC\w+|Sys\w+|SysK\w+)\s+ENDP)");
QRegularExpressionMatch match = syscallRegex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString syscall = match.captured(1);
- if (syscall.startsWith("SC")) {
+
+ if (syscall.startsWith("SC"))
+ {
syscall = syscallPrefix + syscall.mid(2);
}
- if (syscallMap.contains(syscall)) {
+
+ if (syscallMap.contains(syscall))
+ {
QString obfuscatedName = syscallMap.value(syscall);
- if (enableControlFlow && !functionSuffix.isEmpty()) {
+
+ if (enableControlFlow && !functionSuffix.isEmpty())
+ {
obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffix);
}
+
line = line.replace(match.captured(1), obfuscatedName);
}
}
+
newContent << line;
skipRest = false; // reset the flag after processing ENDP
}
+
continue;
}
+
QString line = originalLine;
- if (line.contains(" PROC") || line.contains(" ENDP")) {
+
+ if (line.contains(" PROC") || line.contains(" ENDP"))
+ {
QRegularExpression syscallRegex(R"((SC\w+|Sys\w+|SysK\w+)\s+(PROC|ENDP))");
QRegularExpressionMatch match = syscallRegex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
QString syscall = match.captured(1);
- if (syscall.startsWith("SC")) {
+
+ if (syscall.startsWith("SC"))
+ {
syscall = syscallPrefix + syscall.mid(2);
}
- if (syscallMap.contains(syscall)) {
+
+ if (syscallMap.contains(syscall))
+ {
QString obfuscatedName = syscallMap.value(syscall);
- if (enableControlFlow && !functionSuffix.isEmpty()) {
+
+ if (enableControlFlow && !functionSuffix.isEmpty())
+ {
obfuscatedName = QString("%1_%2").arg(obfuscatedName).arg(functionSuffix);
}
+
line = line.replace(match.captured(1), obfuscatedName);
}
}
- } else if (line.contains("mov eax,") && stubLines.join("").contains("syscall")) {
- if (syscallOffsets.contains(originalSyscall)) {
+ }
+ else if (line.contains("mov eax,") && stubLines.join("").contains("syscall"))
+ {
+ if (syscallOffsets.contains(originalSyscall))
+ {
int realOffset = syscallOffsets.value(originalSyscall);
- if (realToFakeOffset.contains(realOffset)) {
+
+ if (realToFakeOffset.contains(realOffset))
+ {
int fakeOffset = realToFakeOffset.value(realOffset);
QString offsetName = offsetNameMap.value(fakeOffset);
QMap encryptionData = encryptionDataMap.value(offsetName);
- line = stubGen.generateChunkedSequence(offsetName, encryptionData, encryptionMethod);
+ line = stubGen.generateChunkedSequence(offsetName, encryptionData, static_cast(encryptionMethod));
newContent << line;
skipRest = true; // skip original syscall/ret
continue;
}
}
}
+
newContent << line;
}
- if (enableInterleaved) {
+
+ if (enableInterleaved)
+ {
newContent << stubGen.generateAlignPadding();
}
}
newContent << "\nend\n";
+
QFile outAsmFile(asmPath);
- if (!outAsmFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (!outAsmFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to write Assembly File: %1").arg(asmPath) + Colors::ENDC());
return false;
}
+
QStringList cleanedContent;
bool prevEmpty = false;
QString prevLine;
bool foundSyscallRet = false;
QSet seenEndps;
- for (int i = 0; i < newContent.size(); i++) {
+
+ for (int i = 0; i < newContent.size(); i++)
+ {
QString line = newContent[i];
- if (line.contains("ENDP")) {
+
+ if (line.contains("ENDP"))
+ {
QString endpName = line.trimmed();
- if (seenEndps.contains(endpName)) {
+
+ if (seenEndps.contains(endpName))
+ {
continue;
}
+
seenEndps.insert(endpName);
}
- if (line.trimmed() == "syscall") {
+
+ if (line.trimmed() == "syscall")
+ {
int nextIdx = i + 1;
- while (nextIdx < newContent.size() && newContent[nextIdx].trimmed().isEmpty()) {
+
+ while (nextIdx < newContent.size() && newContent[nextIdx].trimmed().isEmpty())
+ {
nextIdx++;
}
- if (nextIdx < newContent.size() && newContent[nextIdx].trimmed() == "ret") {
- if (foundSyscallRet) {
+
+ if (nextIdx < newContent.size() && newContent[nextIdx].trimmed() == "ret")
+ {
+ if (foundSyscallRet)
+ {
i = nextIdx;
continue;
- } else {
+ }
+ else
+ {
cleanedContent << line;
cleanedContent << newContent[nextIdx];
foundSyscallRet = true;
@@ -377,96 +554,143 @@ bool Obfuscation::processAssemblyFile(const QString& asmPath, const QString& hea
}
}
}
- if (line.contains("PROC") || line.contains("ENDP")) {
+
+ if (line.contains("PROC") || line.contains("ENDP"))
+ {
foundSyscallRet = false;
}
- if (line.trimmed() == "ret") {
+
+ if (line.trimmed() == "ret")
+ {
cleanedContent << line;
int j = i + 1;
- while (j < newContent.size() && newContent[j].trimmed().isEmpty()) {
+
+ while (j < newContent.size() && newContent[j].trimmed().isEmpty())
+ {
j++;
}
- if (j < newContent.size() && newContent[j].contains("ENDP")) {
+
+ if (j < newContent.size() && newContent[j].contains("ENDP"))
+ {
i = j - 1;
prevEmpty = false;
continue;
}
}
- if (line.contains("ENDP")) {
+
+ if (line.contains("ENDP"))
+ {
cleanedContent << line;
int j = i + 1;
- while (j < newContent.size() && newContent[j].trimmed().isEmpty()) {
+
+ while (j < newContent.size() && newContent[j].trimmed().isEmpty())
+ {
j++;
}
- if (j < newContent.size() && newContent[j].contains("PROC")) {
+
+ if (j < newContent.size() && newContent[j].contains("PROC"))
+ {
cleanedContent << "";
i = j - 1;
}
+
prevEmpty = false;
continue;
}
- if (line.trimmed() == "syscall") {
+
+ if (line.trimmed() == "syscall")
+ {
int j = i + 1;
bool blankFound = false;
- while (j < newContent.size() && newContent[j].trimmed().isEmpty()) {
+
+ while (j < newContent.size() && newContent[j].trimmed().isEmpty())
+ {
blankFound = true;
j++;
}
- if (j < newContent.size() && newContent[j].trimmed() == "ret") {
+
+ if (j < newContent.size() && newContent[j].trimmed() == "ret")
+ {
cleanedContent << line;
cleanedContent << newContent[j];
i = j;
continue;
}
}
- if (line.trimmed().isEmpty()) {
- if (!prevEmpty) {
+
+ if (line.trimmed().isEmpty())
+ {
+ if (!prevEmpty)
+ {
cleanedContent << line;
prevEmpty = true;
}
- } else {
+ }
+ else
+ {
cleanedContent << line;
prevEmpty = false;
}
+
prevLine = line;
}
+
QTextStream out(&outAsmFile);
out << cleanedContent.join("\n");
outAsmFile.close();
- if (!updateHeaderFile(headerPath, syscallMap, functionSuffixes)) {
+
+ if (!updateHeaderFile(headerPath, syscallMap, functionSuffixes))
+ {
logMessage(Colors::FAIL() + "Failed to update Header File" + Colors::ENDC());
return false;
}
+
bool bindingsEnabled = settings->value("general/bindings_enabled", false).toBool();
- if (bindingsEnabled && !isKernelMode()) {
+
+ if (bindingsEnabled && !isKernelMode())
+ {
QString defPath = getDefFilePath(isKernelMode());
QStringList obfuscatedNames;
QRegularExpression procRegex(R"(\s*([A-Za-z0-9_]+)\s+PROC)");
- for (const QString& line : newContent) {
+
+ for (const QString& line : newContent)
+ {
QRegularExpressionMatch match = procRegex.match(line);
- if (match.hasMatch()) {
+
+ if (match.hasMatch())
+ {
obfuscatedNames << match.captured(1);
}
}
- if (!updateDefFile(defPath, obfuscatedNames)) {
+
+ if (!updateDefFile(defPath, obfuscatedNames))
+ {
logMessage(Colors::FAIL() + "Failed to update DEF File" + Colors::ENDC());
return false;
}
}
+
logMessage(Colors::OKGREEN() + QString("Generated %1 unique Syscalls with Obfuscated Names, Offsets, and Junk Instructions")
- .arg(syscallMap.size()) + Colors::ENDC());
+ .arg(syscallMap.size()) + Colors::ENDC());
return true;
}
-bool Obfuscation::updateHeaderFile(const QString& headerPath, const QMap& syscallMap, const QMap& functionSuffixes) {
+bool Obfuscation::updateHeaderFile(const QString& headerPath,
+ const QMap& syscallMap,
+ const QMap& functionSuffixes)
+{
QFile headerFile(headerPath);
- if (!headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (!headerFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
logMessage(Colors::FAIL() + QString("Failed to open Header File: %1").arg(headerPath) + Colors::ENDC());
return false;
}
+
QTextStream in(&headerFile);
QStringList headerContent = in.readAll().split('\n');
headerFile.close();
+
QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList();
bool useAllSyscalls = selectedSyscalls.isEmpty();
bool enableControlFlow = settings->value("obfuscation/control_flow_enabled", false).toBool();
@@ -475,7 +699,9 @@ bool Obfuscation::updateHeaderFile(const QString& headerPath, const QMap
#include
-SharedObfuscation::NameGenerator::NameGenerator(QSettings* settings) : settings(settings) {
-}
+SharedObfuscation::NameGenerator::NameGenerator(QSettings* settings)
+ : settings(settings)
+{}
-void SharedObfuscation::NameGenerator::setSettings(QSettings* settings) {
+void SharedObfuscation::NameGenerator::setSettings(QSettings* settings)
+{
this->settings = settings;
}
-QString SharedObfuscation::NameGenerator::generateRandomString(int length) {
+QString SharedObfuscation::NameGenerator::generateRandomString(int length)
+{
const QString chars = "abcdefghijklmnopqrstuvwxyz";
QString result;
- for (int i = 0; i < length; ++i) {
+
+ for (int i = 0; i < length; ++i)
+ {
result += chars[getRandomInt(0, chars.length() - 1)];
}
+
return result;
}
-QString SharedObfuscation::NameGenerator::generateRandomName(QSet& usedNames, int prefixLength, int numberLength) {
- if (!settings) {
+QString SharedObfuscation::NameGenerator::generateRandomName(QSet& usedNames,
+ int prefixLength,
+ int numberLength)
+{
+ if (!settings)
+ {
return "";
}
- if (prefixLength == -1) {
+
+ if (prefixLength == -1)
+ {
prefixLength = settings->value("obfuscation/syscall_prefix_length", 8).toInt();
}
- if (numberLength == -1) {
+
+ if (numberLength == -1)
+ {
numberLength = settings->value("obfuscation/syscall_number_length", 6).toInt();
}
+
QString name;
- do {
+
+ do
+ {
QString prefix = generateRandomString(prefixLength);
int minNumber = qPow(10, numberLength - 1);
int maxNumber = qPow(10, numberLength) - 1;
int number = getRandomInt(minNumber, maxNumber);
name = QString("%1_%2").arg(prefix).arg(number);
- } while (usedNames.contains(name));
+ }
+ while (usedNames.contains(name));
+
usedNames.insert(name);
return name;
}
-QString SharedObfuscation::NameGenerator::generateRandomOffsetName(QSet& usedNames, int length) {
- if (!settings) {
+QString SharedObfuscation::NameGenerator::generateRandomOffsetName(QSet& usedNames,
+ int length)
+{
+ if (!settings)
+ {
return "";
}
- if (length == -1) {
+
+ if (length == -1)
+ {
length = settings->value("obfuscation/offset_name_length", 8).toInt();
}
+
QString name;
- do {
+
+ do
+ {
name = generateRandomString(length);
- } while (usedNames.contains(name));
+ }
+ while (usedNames.contains(name));
+
usedNames.insert(name);
return name;
}
-int SharedObfuscation::NameGenerator::generateRandomOffset(QSet& usedOffsets) {
+int SharedObfuscation::NameGenerator::generateRandomOffset(QSet& usedOffsets)
+{
int offset;
- do {
+
+ do
+ {
offset = getRandomInt(0x1000, 0xFFFF);
} while (usedOffsets.contains(offset));
+
usedOffsets.insert(offset);
return offset;
}
-QString SharedObfuscation::NameGenerator::generateRandomLabel() {
+QString SharedObfuscation::NameGenerator::generateRandomLabel()
+{
return generateRandomString(8);
}
-int SharedObfuscation::NameGenerator::getRandomInt(int min, int max) {
+int SharedObfuscation::NameGenerator::getRandomInt(int min, int max)
+{
return QRandomGenerator::global()->bounded(min, max + 1);
}
diff --git a/Bind/src/Core/Utils/PathUtils.cpp b/Bind/src/Core/Utils/PathUtils.cpp
index 33ef01b..8562571 100644
--- a/Bind/src/Core/Utils/PathUtils.cpp
+++ b/Bind/src/Core/Utils/PathUtils.cpp
@@ -15,143 +15,202 @@
static QString s_projectRoot;
-QString PathUtils::getProjectRoot() {
- if (s_projectRoot.isEmpty()) {
+QString PathUtils::getProjectRoot()
+{
+ if (s_projectRoot.isEmpty())
+ {
s_projectRoot = findProjectRoot();
}
+
return s_projectRoot;
}
-QString PathUtils::getBackupsPath() {
+QString PathUtils::getBackupsPath()
+{
return getProjectRoot() + "/Backups";
}
-QString PathUtils::getHashBackupsPath() {
+QString PathUtils::getHashBackupsPath()
+{
return getBackupsPath() + "/Hashes";
}
-QString PathUtils::getDefaultPath() {
+QString PathUtils::getDefaultPath()
+{
return getProjectRoot() + "/Default";
}
-QString PathUtils::getSysCallerPath() {
+QString PathUtils::getSysCallerPath()
+{
return getProjectRoot() + "/SysCaller";
}
-QString PathUtils::getSysCallerKPath() {
+QString PathUtils::getSysCallerKPath()
+{
return getProjectRoot() + "/SysCallerK";
}
-QString PathUtils::getSysFunctionsPath(bool isKernelMode) {
- if (isKernelMode) {
+QString PathUtils::getSysFunctionsPath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return getSysCallerKPath() + "/Wrapper/include/SysK/sysFunctions_k.h";
- } else {
+ }
+ else
+ {
return getSysCallerPath() + "/Wrapper/include/Sys/sysFunctions.h";
}
}
-QString PathUtils::getSysCallerAsmPath(bool isKernelMode) {
- if (isKernelMode) {
+QString PathUtils::getSysCallerAsmPath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return getSysCallerKPath() + "/Wrapper/src/syscaller.asm";
- } else {
+ }
+ else
+ {
return getSysCallerPath() + "/Wrapper/src/syscaller.asm";
}
}
-QString PathUtils::getDefaultSysFunctionsPath(bool isKernelMode) {
- if (isKernelMode) {
+QString PathUtils::getDefaultSysFunctionsPath(bool isKernelMode)
+{
+ if (isKernelMode)
+ {
return getDefaultPath() + "/sysFunctions_k.h";
- } else {
+ }
+ else
+ {
return getDefaultPath() + "/sysFunctions.h";
}
}
-QString PathUtils::getDefaultSysCallerAsmPath() {
+QString PathUtils::getDefaultSysCallerAsmPath()
+{
return getDefaultPath() + "/syscaller.asm";
}
-QString PathUtils::getIniPath() {
+QString PathUtils::getIniPath()
+{
return getProjectRoot() + "/SysCaller.ini";
}
-QString PathUtils::findProjectRoot() {
+QString PathUtils::findProjectRoot()
+{
QDir dir(QApplication::applicationDirPath());
qDebug() << "Starting Path Resolution from:" << dir.absolutePath();
- while (!dir.isRoot()) {
+
+ while (!dir.isRoot())
+ {
QString currentPath = QDir::cleanPath(dir.absolutePath());
QFileInfo info(currentPath);
qDebug() << "Checking Directory:" << currentPath << "Name:" << info.fileName();
- if (info.fileName() == "SysCaller") {
+
+ if (info.fileName() == "SysCaller")
+ {
qDebug() << "Found SysCaller Directory:" << currentPath;
- if (isProjectRoot(currentPath)) {
+
+ if (isProjectRoot(currentPath))
+ {
qDebug() << "Found Project Root (validated):" << currentPath;
return currentPath;
- } else {
+ }
+ else
+ {
qDebug() << "SysCaller Directory found but Validation failed";
}
}
+
QDir parentDir = dir;
parentDir.cdUp();
QString parentPath = QDir::cleanPath(parentDir.absolutePath());
QFileInfo parentInfo(parentPath);
- if (parentInfo.fileName() == "SysCaller") {
+
+ if (parentInfo.fileName() == "SysCaller")
+ {
qDebug() << "Found SysCaller Parent Directory:" << parentPath;
- if (isProjectRoot(parentPath)) {
+
+ if (isProjectRoot(parentPath))
+ {
qDebug() << "Found Project Root (Validated):" << parentPath;
return parentPath;
}
}
+
dir.cdUp();
}
QDir dir2(QApplication::applicationDirPath());
- while (!dir2.isRoot()) {
+
+ while (!dir2.isRoot())
+ {
QString currentPath = QDir::cleanPath(dir2.absolutePath());
QFileInfo info(currentPath);
- if (info.fileName() == "Bind") {
+
+ if (info.fileName() == "Bind")
+ {
qDebug() << "Found Bind Directory:" << currentPath;
+
QDir projectRootDir = dir2;
projectRootDir.cdUp();
QString projectRootPath = QDir::cleanPath(projectRootDir.absolutePath());
qDebug() << "Checking Potential Project Root:" << projectRootPath;
- if (isProjectRoot(projectRootPath)) {
+
+ if (isProjectRoot(projectRootPath))
+ {
qDebug() << "Found Project Root via Bind:" << projectRootPath;
return projectRootPath;
}
}
+
dir2.cdUp();
}
+
QString hardcodedPath = "C:/Users/devil/source/repos/SysCaller";
- if (QDir(hardcodedPath).exists() && isProjectRoot(hardcodedPath)) {
+
+ if (QDir(hardcodedPath).exists() && isProjectRoot(hardcodedPath))
+ {
qDebug() << "Using Hardcoded Project Root:" << hardcodedPath;
return hardcodedPath;
}
+
qWarning() << "Project Root not found, falling back to executable directory";
return QApplication::applicationDirPath();
}
-bool PathUtils::isProjectRoot(const QString& path) {
+bool PathUtils::isProjectRoot(const QString& path)
+{
QDir dir(path);
- QStringList requiredItems = {
+ QStringList requiredItems =
+ {
"SysCaller",
"SysCallerK",
"Backups",
"Bindings"
};
+
int foundItems = 0;
- for (const QString& item : requiredItems) {
- if (dir.exists(item)) {
+
+ for (const QString& item : requiredItems)
+ {
+ if (dir.exists(item))
+ {
foundItems++;
qDebug() << "Found Project Root Item:" << item;
- } else {
+ }
+ else
+ {
qDebug() << "Missing Project Root Item:" << item;
}
}
+
bool isValid = (foundItems >= 3);
qDebug() << "Project Root Validation Result:" << isValid << "(" << foundItems << "/" << requiredItems.size() << " Items Found)";
return isValid;
}
-void PathUtils::debugPathDetection() {
+void PathUtils::debugPathDetection()
+{
qDebug() << "=== PathUtils Debug Information ===";
qDebug() << "Application Directory:" << QApplication::applicationDirPath();
qDebug() << "Project Root:" << getProjectRoot();
diff --git a/Bind/src/Core/Utils/Utils.cpp b/Bind/src/Core/Utils/Utils.cpp
index 2e2f001..479a139 100644
--- a/Bind/src/Core/Utils/Utils.cpp
+++ b/Bind/src/Core/Utils/Utils.cpp
@@ -1,5 +1,7 @@
#include "include/Core/Utils/Utils.h"
#include "include/Core/Utils/PathUtils.h"
+#include "include/Core/Obfuscation/Obfuscation.h"
+#include "include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h"
#include
#include
#include
@@ -14,91 +16,146 @@
#include
#include
-QMap SyscallExtractor::getSyscallsFromDll(const QString& dllPath) {
+QMap SyscallExtractor::getSyscallsFromDll(const QString& dllPath)
+{
QMap syscallNumbers;
qDebug() << QString("Starting to parse DLL: %1").arg(dllPath);
+
QByteArray dllPathBytes = dllPath.toLocal8Bit();
const char* dllPathCStr = dllPathBytes.constData();
+
qDebug() << "DLL Path as Const Char*:" << dllPathCStr;
+
peparse::parsed_pe* pe = peparse::ParsePEFromFile(dllPathCStr);
- if (!pe) {
+
+ if (!pe)
+ {
qWarning() << "Failed to parse PE File:" << dllPath;
return syscallNumbers;
}
+
uint64_t imageBase = pe->peHeader.nt.OptionalHeader64.ImageBase;
qDebug() << "Image Base:" << QString("0x%1").arg(imageBase, 0, 16);
+
static int exportCount = 0;
exportCount = 0;
- struct CallbackData {
+
+ struct CallbackData
+ {
QMap* syscallNumbers;
peparse::parsed_pe* pe;
uint64_t imageBase;
};
+
CallbackData callbackData = { &syscallNumbers, pe, imageBase };
- auto callback = [](void* N, const peparse::VA& addr, const std::string& mod, const std::string& fn) -> int {
+
+ auto callback = [](void* N, const peparse::VA& addr, const std::string& mod, const std::string& fn) -> int
+ {
auto* data = static_cast(N);
- if (fn.empty()) {
+
+ if (fn.empty())
+ {
return 0;
}
+
QString funcName;
- try {
+
+ try
+ {
funcName = QString::fromUtf8(fn.c_str(), fn.length());
- } catch (...) {
+ }
+ catch (...)
+ {
qDebug() << "Failed to convert Function Name, skipping.";
return 0;
}
+
static int exportCount = 0;
exportCount++;
- if (exportCount % 100 == 0) {
+
+ if (exportCount % 100 == 0)
+ {
qDebug() << "Processed" << exportCount << "Exports...";
}
- if (!funcName.startsWith("Nt") && !funcName.startsWith("Zw")) {
+
+ if (!funcName.startsWith("Nt") && !funcName.startsWith("Zw"))
+ {
return 0;
}
+
static int processedCount = 0;
- if (processedCount < 3) {
+
+ if (processedCount < 3)
+ {
qDebug() << "Processing Function:" << funcName;
processedCount++;
}
+
uint32_t funcRVA = static_cast(addr - data->imageBase);
uint32_t fileOffset = 0;
- if (addr < data->imageBase) {
+
+ if (addr < data->imageBase)
+ {
qDebug() << "Invalid Address, Skipping Function:" << funcName;
return 0;
}
- if (SyscallExtractor::rvaToFileOffset(data->pe, funcRVA, fileOffset)) {
- if (!data->pe || !data->pe->fileBuffer) {
+
+ if (SyscallExtractor::rvaToFileOffset(data->pe, funcRVA, fileOffset))
+ {
+ if (!data->pe || !data->pe->fileBuffer)
+ {
qDebug() << "PE File or buffer is null, skipping Function:" << funcName;
return 0;
}
+
std::vector funcBytes;
size_t bytesRead = SyscallExtractor::readBytesFromBuffer(data->pe->fileBuffer, fileOffset, 32, funcBytes);
- if (bytesRead > 0) {
+
+ if (bytesRead > 0)
+ {
static int debugCount = 0;
- if (debugCount < 3) {
+
+ if (debugCount < 3)
+ {
QString bytesHex;
- for (size_t i = 0; i < qMin(bytesRead, size_t(16)); ++i) {
+
+ for (size_t i = 0; i < qMin(bytesRead, size_t(16)); ++i)
+ {
bytesHex += QString("%1 ").arg(funcBytes[i], 2, 16, QChar('0'));
}
- qDebug() << "Function:" << funcName << "RVA:" << QString("0x%1").arg(funcRVA, 0, 16) << "File Offset:" << QString("0x%1").arg(fileOffset, 0, 16);
+
+ qDebug() << "Function:" << funcName
+ << "RVA:" << QString("0x%1").arg(funcRVA, 0, 16)
+ << "File Offset:" << QString("0x%1").arg(fileOffset, 0, 16);
qDebug() << "First 16 Bytes:" << bytesHex;
debugCount++;
}
+
bool foundSyscall = false;
- if (bytesRead >= 8) {
- for (size_t i = 0; i <= bytesRead - 8; ++i) {
- if (funcBytes[i] == 0x4c && funcBytes[i+1] == 0x8b && funcBytes[i+2] == 0xd1) {
- if (funcBytes[i+3] == 0xb8) {
- uint32_t syscallId = funcBytes[i+4] |
- (funcBytes[i+5] << 8) |
- (funcBytes[i+6] << 16) |
+
+ if (bytesRead >= 8)
+ {
+ for (size_t i = 0; i <= bytesRead - 8; ++i)
+ {
+ if (funcBytes[i] == 0x4c && funcBytes[i+1] == 0x8b && funcBytes[i+2] == 0xd1)
+ {
+ if (funcBytes[i+3] == 0xb8)
+ {
+ uint32_t syscallId = funcBytes[i+4] |
+ (funcBytes[i+5] << 8) |
+ (funcBytes[i+6] << 16) |
(funcBytes[i+7] << 24);
- if (syscallId <= 0xFFFF) {
+
+ if (syscallId <= 0xFFFF)
+ {
static int syscallLogCount = 0;
- if (syscallLogCount < 5) {
+
+ if (syscallLogCount < 5)
+ {
qDebug() << "Found Syscall ID:" << syscallId << "For" << funcName;
syscallLogCount++;
}
+
(*data->syscallNumbers)[funcName] = static_cast(syscallId);
foundSyscall = true;
break;
@@ -106,158 +163,234 @@ QMap SyscallExtractor::getSyscallsFromDll(const QString& dllPath)
}
}
}
- if (!foundSyscall) {
+
+ if (!foundSyscall)
+ {
static int failureLogCount = 0;
- if (failureLogCount < 3) {
+
+ if (failureLogCount < 3)
+ {
qDebug() << "Syscall Pattern not found for" << funcName;
failureLogCount++;
}
}
- } else {
+ }
+ else
+ {
qDebug() << "Not enough bytes to parse Syscall Pattern for" << funcName;
}
- } else {
+ }
+ else
+ {
qDebug() << "Failed to read bytes for Function:" << funcName;
}
- } else {
+ }
+ else
+ {
qDebug() << "Failed to convert RVA to File Offset for Function:" << funcName;
}
+
return 0;
};
peparse::IterExpVA(pe, callback, &callbackData);
-
+
peparse::DestructParsedPE(pe);
qDebug() << QString("Finished parsing DLL, found %1 Syscalls").arg(syscallNumbers.size());
- if (syscallNumbers.size() > 0) {
+
+ if (syscallNumbers.size() > 0)
+ {
qDebug() << "Sample Syscalls Found:";
int count = 0;
- for (auto it = syscallNumbers.begin(); it != syscallNumbers.end() && count < 5; ++it, ++count) {
+
+ for (auto it = syscallNumbers.begin(); it != syscallNumbers.end() && count < 5; ++it, ++count)
+ {
qDebug() << " " << it.key() << "->" << it.value();
}
}
+
return syscallNumbers;
}
-bool SyscallExtractor::rvaToFileOffset(peparse::parsed_pe* pe, uint32_t rva, uint32_t& fileOffset) {
- struct CallbackData {
+bool SyscallExtractor::rvaToFileOffset(peparse::parsed_pe* pe, uint32_t rva, uint32_t& fileOffset)
+{
+ struct CallbackData
+ {
bool found;
uint32_t address;
uint32_t result;
} data{false, rva, 0};
+
auto L_inspectSection = [](void* N,
const peparse::VA& secBase,
const std::string& secName,
const peparse::image_section_header& s,
- const peparse::bounded_buffer* dataSec) -> int {
+ const peparse::bounded_buffer* dataSec) -> int
+ {
static_cast(secBase);
static_cast(secName);
static_cast(dataSec);
+
auto callback_data = static_cast(N);
uint32_t sectionBaseAddress = s.VirtualAddress;
uint32_t sectionSize;
- if (s.SizeOfRawData != 0) {
+
+ if (s.SizeOfRawData != 0)
+ {
sectionSize = s.SizeOfRawData;
- } else {
+ }
+ else
+ {
sectionSize = s.Misc.VirtualSize;
}
+
uint32_t sectionEndAddress = sectionBaseAddress + sectionSize;
+
if (callback_data->address >= sectionBaseAddress &&
- callback_data->address < sectionEndAddress) {
+ callback_data->address < sectionEndAddress)
+ {
callback_data->result = s.PointerToRawData + (callback_data->address - sectionBaseAddress);
callback_data->found = true;
- qDebug() << "Found in Section:" << QString::fromStdString(secName)
+
+ qDebug() << "Found in Section:" << QString::fromStdString(secName)
<< "RVA:" << QString("0x%1").arg(callback_data->address, 0, 16)
<< "-> File Offset:" << QString("0x%1").arg(callback_data->result, 0, 16);
return 1;
}
+
return 0;
};
+
peparse::IterSec(pe, L_inspectSection, &data);
- if (data.found) {
+
+ if (data.found)
+ {
fileOffset = data.result;
return true;
}
+
qDebug() << "Failed to convert RVA" << QString("0x%1").arg(rva, 0, 16) << "to File Offset";
return false;
}
-size_t SyscallExtractor::readBytesFromBuffer(const peparse::bounded_buffer* buffer, uint32_t offset, size_t size, std::vector& data) {
- if (!buffer) {
+size_t SyscallExtractor::readBytesFromBuffer(const peparse::bounded_buffer* buffer, uint32_t offset, size_t size, std::vector& data)
+{
+ if (!buffer)
+ {
qDebug() << "Buffer is Null";
return 0;
}
- if (offset >= buffer->bufLen) {
- qDebug() << "Offset" << QString("0x%1").arg(offset, 0, 16) << ">= Buffer Length" << QString("0x%1").arg(buffer->bufLen, 0, 16);
+
+ if (offset >= buffer->bufLen)
+ {
+ qDebug() << "Offset" << QString("0x%1").arg(offset, 0, 16)
+ << ">= Buffer Length" << QString("0x%1").arg(buffer->bufLen, 0, 16);
return 0;
}
- if (offset + size > buffer->bufLen) {
- qDebug() << "Offset + Size" << QString("0x%1").arg(offset + size, 0, 16) << "> Buffer Length" << QString("0x%1").arg(buffer->bufLen, 0, 16);
+
+ if (offset + size > buffer->bufLen)
+ {
+ qDebug() << "Offset + Size" << QString("0x%1").arg(offset + size, 0, 16)
+ << "> Buffer Length" << QString("0x%1").arg(buffer->bufLen, 0, 16);
return 0;
}
+
data.resize(size);
memcpy(data.data(), buffer->buf + offset, size);
- qDebug() << "Successfully read" << size << "bytes from Offset" << QString("0x%1").arg(offset, 0, 16);
+
+ qDebug() << "Successfully read" << size << "bytes from Offset"
+ << QString("0x%1").arg(offset, 0, 16);
+
return size;
}
-QVariantMap StubHashGenerator::generateStubHashes(const QString& asmFilePath, const QString& headerFilePath, const QString& obfuscationMethod) {
+QVariantMap StubHashGenerator::generateStubHashes(const QString& asmFilePath,
+ const QString& headerFilePath,
+ const QString& obfuscationMethod)
+{
QVariantMap stubHashes;
+
qDebug() << "Generating Stub Hashes...";
qDebug() << " ASM File:" << asmFilePath;
qDebug() << " Header File:" << headerFilePath;
qDebug() << " Obfuscation Method:" << obfuscationMethod;
- try {
+ try
+ {
QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat);
- bool usingStubMapper = false;
- if (!obfuscationMethod.isEmpty()) {
- usingStubMapper = (obfuscationMethod == "stub_mapper");
- } else {
+ QMap syscallSettings;
+
+ ObfuscationMode currentMode = ObfuscationMode::Normal;
+
+ if (!obfuscationMethod.isEmpty())
+ {
+ currentMode = stringToObfuscationMode(obfuscationMethod);
+ }
+ else
+ {
bool forceStubMapper = settings.value("obfuscation/force_stub_mapper", false).toBool();
bool forceNormal = settings.value("obfuscation/force_normal", false).toBool();
- QMap syscallSettings = settings.value("stub_mapper/syscall_settings", QMap()).toMap();
- usingStubMapper = forceStubMapper || (syscallSettings.size() > 0 && !forceNormal);
+ syscallSettings = settings.value("stub_mapper/syscall_settings",
+ QMap()).toMap();
+
+ currentMode = (forceStubMapper || (syscallSettings.size() > 0 && !forceNormal))
+ ? ObfuscationMode::StubMapper : ObfuscationMode::Normal;
}
- QMap syscallSettings = settings.value("stub_mapper/syscall_settings", QMap()).toMap();
+
QVariantMap config;
- if (usingStubMapper && syscallSettings.size() > 0) {
+
+ if (currentMode == ObfuscationMode::StubMapper && syscallSettings.size() > 0)
+ {
config["obfuscation_method"] = "Stub Mapper";
QVariantMap globalSettings;
QVariantMap junkInstructions;
junkInstructions["min"] = settings.value("obfuscation/min_instructions", 2).toInt();
junkInstructions["max"] = settings.value("obfuscation/max_instructions", 8).toInt();
junkInstructions["advanced"] = settings.value("obfuscation/use_advanced_junk", false).toBool();
+
globalSettings["junk_instructions"] = junkInstructions;
+
QVariantMap nameRandomization;
nameRandomization["prefix_length"] = settings.value("obfuscation/syscall_prefix_length", 8).toInt();
nameRandomization["number_length"] = settings.value("obfuscation/syscall_number_length", 6).toInt();
nameRandomization["offset_length"] = settings.value("obfuscation/offset_name_length", 8).toInt();
+
globalSettings["name_randomization"] = nameRandomization;
globalSettings["sequence_shuffling"] = settings.value("obfuscation/shuffle_sequence", true).toBool();
+
QVariantMap encryption;
encryption["enabled"] = settings.value("obfuscation/enable_encryption", true).toBool();
- encryption["method"] = settings.value("obfuscation/encryption_method", 1).toInt();
+ encryption["method"] = static_cast(DirectObfuscation::EncryptionMethod::BasicXOR);
+
globalSettings["encryption"] = encryption;
globalSettings["function_chunking"] = settings.value("obfuscation/enable_chunking", true).toBool();
globalSettings["interleaved_execution"] = settings.value("obfuscation/enable_interleaved", true).toBool();
+
config["global_settings"] = globalSettings;
config["syscall_specific_settings"] = syscallSettings;
- } else {
- config["obfuscation_method"] = "Normal";
+ }
+ else
+ {
+ config["obfuscation_method"] = obfuscationModeToString(currentMode);
+
QVariantMap junkInstructions;
junkInstructions["min"] = settings.value("obfuscation/min_instructions", 2).toInt();
junkInstructions["max"] = settings.value("obfuscation/max_instructions", 8).toInt();
junkInstructions["advanced"] = settings.value("obfuscation/use_advanced_junk", false).toBool();
+
config["junk_instructions"] = junkInstructions;
+
QVariantMap nameRandomization;
nameRandomization["prefix_length"] = settings.value("obfuscation/syscall_prefix_length", 8).toInt();
nameRandomization["number_length"] = settings.value("obfuscation/syscall_number_length", 6).toInt();
nameRandomization["offset_length"] = settings.value("obfuscation/offset_name_length", 8).toInt();
+
config["name_randomization"] = nameRandomization;
config["sequence_shuffling"] = settings.value("obfuscation/shuffle_sequence", true).toBool();
+
QVariantMap encryption;
encryption["enabled"] = settings.value("obfuscation/enable_encryption", true).toBool();
- encryption["method"] = settings.value("obfuscation/encryption_method", 1).toInt();
+ encryption["method"] = static_cast(DirectObfuscation::EncryptionMethod::BasicXOR);
+
config["encryption"] = encryption;
config["function_chunking"] = settings.value("obfuscation/enable_chunking", true).toBool();
config["interleaved_execution"] = settings.value("obfuscation/enable_interleaved", true).toBool();
@@ -265,153 +398,234 @@ QVariantMap StubHashGenerator::generateStubHashes(const QString& asmFilePath, co
stubHashes["timestamp"] = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
stubHashes["config"] = config;
stubHashes["stubs"] = QVariantMap();
+
QFile asmFile(asmFilePath);
- if (asmFile.exists() && asmFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (asmFile.exists() && asmFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
QString asmContent = asmFile.readAll();
asmFile.close();
+
qDebug() << "Processing ASM File:" << asmFilePath;
qDebug() << "ASM Content Length:" << asmContent.length();
+
QMap obfuscatedToSyscall;
- QRegularExpression aliasRegex("ALIAS\\s+<(Sys[A-Za-z0-9_]+)>\\s*=\\s*<([A-Za-z0-9_]+)>", QRegularExpression::CaseInsensitiveOption);
+ QRegularExpression aliasRegex("ALIAS\\s+<(Sys[A-Za-z0-9_]+)>\\s*=\\s*<([A-Za-z0-9_]+)>",
+ QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatchIterator aliasIterator = aliasRegex.globalMatch(asmContent);
- while (aliasIterator.hasNext()) {
+
+ while (aliasIterator.hasNext())
+ {
QRegularExpressionMatch match = aliasIterator.next();
QString syscall = match.captured(1);
QString obfuscated = match.captured(2);
+
obfuscatedToSyscall[obfuscated] = syscall;
qDebug() << "Found ALIAS:" << syscall << "->" << obfuscated;
}
+
qDebug() << "Found" << obfuscatedToSyscall.size() << "ALIAS Declarations";
- QRegularExpression procRegex("([A-Za-z0-9_]+)\\s+PROC", QRegularExpression::CaseInsensitiveOption);
+
+ QRegularExpression procRegex("([A-Za-z0-9_]+)\\s+PROC",
+ QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatchIterator procIterator = procRegex.globalMatch(asmContent);
+
int procCount = 0;
int matchedProcCount = 0;
- while (procIterator.hasNext()) {
+
+ while (procIterator.hasNext())
+ {
QRegularExpressionMatch match = procIterator.next();
QString procName = match.captured(1);
+
procCount++;
- if (!obfuscatedToSyscall.contains(procName)) {
+
+ if (!obfuscatedToSyscall.contains(procName))
+ {
qDebug() << "PROC not in ALIAS Map:" << procName;
continue;
}
+
matchedProcCount++;
QString syscallName = obfuscatedToSyscall[procName];
int startPos = match.capturedStart();
int endPos = -1;
- QRegularExpression endpRegex(QString("%1\\s+ENDP").arg(procName), QRegularExpression::CaseInsensitiveOption);
+
+ QRegularExpression endpRegex(QString("%1\\s+ENDP").arg(procName),
+ QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch endpMatch = endpRegex.match(asmContent, startPos);
- if (endpMatch.hasMatch()) {
+
+ if (endpMatch.hasMatch())
+ {
endPos = endpMatch.capturedEnd();
}
- if (endPos != -1) {
+
+ if (endPos != -1)
+ {
QString stubCode = asmContent.mid(startPos, endPos - startPos);
+
// generate MD5 hash
QByteArray md5Hash = QCryptographicHash::hash(stubCode.toUtf8(), QCryptographicHash::Md5);
QString md5Hex = md5Hash.toHex();
+
// generate SHA256 hash
QByteArray sha256Hash = QCryptographicHash::hash(stubCode.toUtf8(), QCryptographicHash::Sha256);
QString sha256Hex = sha256Hash.toHex();
+
QVariantMap hashData;
hashData["md5"] = md5Hex;
hashData["sha256"] = sha256Hex;
hashData["size"] = stubCode.length();
hashData["obfuscated_name"] = procName;
- if (usingStubMapper && syscallSettings.contains(syscallName)) {
+
+ if (currentMode == ObfuscationMode::StubMapper && syscallSettings.contains(syscallName))
+ {
hashData["custom_config"] = syscallSettings[syscallName];
}
+
QVariantMap stubs = stubHashes["stubs"].toMap();
stubs[syscallName] = hashData;
stubHashes["stubs"] = stubs;
+
qDebug() << "Added Stub Hash for:" << syscallName << "(" << procName << ")";
}
}
+
qDebug() << "Total PROC Declarations:" << procCount;
qDebug() << "Matched PROC Declarations:" << matchedProcCount;
qDebug() << "Final Stub Count:" << stubHashes["stubs"].toMap().size();
}
QFile headerFile(headerFilePath);
- if (headerFile.exists() && headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (headerFile.exists() && headerFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
QString headerContent = headerFile.readAll();
headerFile.close();
- QRegularExpression funcRegex("EXTERN_C\\s+(?:__kernel_entry\\s+)?(?:NTSYSCALLAPI\\s+)?(?:NTSTATUS|BOOL|VOID|HANDLE|PVOID|ULONG|.*?)\\s+(?:NTAPI|WINAPI)?\\s*(Sys[A-Za-z0-9_]+)\\s*\\(([^;]*)\\);", QRegularExpression::CaseInsensitiveOption);
+
+ QRegularExpression funcRegex("EXTERN_C\\s+(?:__kernel_entry\\s+)?(?:NTSYSCALLAPI\\s+)?(?:NTSTATUS|BOOL|VOID|HANDLE|PVOID|ULONG|.*?)\\s+(?:NTAPI|WINAPI)?\\s*(Sys[A-Za-z0-9_]+)\\s*\\(([^;]*)\\);",
+ QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatchIterator funcIterator = funcRegex.globalMatch(headerContent);
- while (funcIterator.hasNext()) {
+
+ while (funcIterator.hasNext())
+ {
QRegularExpressionMatch match = funcIterator.next();
QString name = match.captured(1);
QString params = match.captured(2);
+
QVariantMap stubs = stubHashes["stubs"].toMap();
- if (stubs.contains(name)) {
+
+ if (stubs.contains(name))
+ {
QVariantMap hashData = stubs[name].toMap();
QByteArray headerHash = QCryptographicHash::hash(params.toUtf8(), QCryptographicHash::Sha256);
+
hashData["header_hash"] = headerHash.toHex();
hashData["params"] = params.trimmed();
+
stubs[name] = hashData;
stubHashes["stubs"] = stubs;
}
}
}
+
return stubHashes;
- } catch (...) {
+ }
+ catch (...)
+ {
QVariantMap error;
error["error"] = "Error Generating Stub Hashes";
return error;
}
}
-QPair StubHashGenerator::saveStubHashes(const QVariantMap& stubHashes, const QString& timestamp) {
- try {
+QPair StubHashGenerator::saveStubHashes(const QVariantMap& stubHashes,
+ const QString& timestamp)
+{
+ try
+ {
QString hashBackupsDir = PathUtils::getHashBackupsPath();
QDir().mkpath(hashBackupsDir);
+
QString actualTimestamp = timestamp;
- if (actualTimestamp.isEmpty()) {
+
+ if (actualTimestamp.isEmpty())
+ {
actualTimestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
}
+
QString jsonPath = QString("%1/stub_hashes_%2.json").arg(hashBackupsDir, actualTimestamp);
+
QVariantMap formattedOutput;
formattedOutput["timestamp"] = stubHashes["timestamp"];
formattedOutput["config"] = stubHashes["config"];
formattedOutput["stubs"] = QVariantMap();
+
QVariantMap stubs = stubHashes["stubs"].toMap();
QVariantMap formattedStubs;
+
qDebug() << "Saving" << stubs.size() << "Stubs";
- for (auto it = stubs.begin(); it != stubs.end(); ++it) {
+
+ for (auto it = stubs.begin(); it != stubs.end(); ++it)
+ {
QString syscallName = it.key();
QVariantMap hashData = it.value().toMap();
- QString formattedHash = QString("MD5: %1 SHA-256: %2").arg(hashData["md5"].toString(), hashData["sha256"].toString());
+ QString formattedHash = QString("MD5: %1 SHA-256: %2")
+ .arg(hashData["md5"].toString(),
+ hashData["sha256"].toString());
+
formattedStubs[syscallName] = formattedHash;
qDebug() << "Formatted Stub:" << syscallName << "->" << formattedHash;
}
+
formattedOutput["stubs"] = formattedStubs;
+
// generate build ID
QStringList allHashes;
QStringList sortedSyscalls = stubs.keys();
std::sort(sortedSyscalls.begin(), sortedSyscalls.end());
- for (const QString& syscallName : sortedSyscalls) {
+
+ for (const QString& syscallName : sortedSyscalls)
+ {
QVariantMap hashData = stubs[syscallName].toMap();
- allHashes << QString("%1:%2:%3").arg(syscallName, hashData["md5"].toString(), hashData["sha256"].toString());
+ allHashes << QString("%1:%2:%3")
+ .arg(syscallName,
+ hashData["md5"].toString(),
+ hashData["sha256"].toString());
}
+
QJsonDocument configDoc = QJsonDocument::fromVariant(stubHashes["config"]);
QString buildIdInput = allHashes.join(":") + QString(configDoc.toJson());
QByteArray buildIdHash = QCryptographicHash::hash(buildIdInput.toUtf8(), QCryptographicHash::Sha256);
+
formattedOutput["build_id"] = buildIdHash.toHex();
+
QFile jsonFile(jsonPath);
- if (jsonFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
+
+ if (jsonFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
QJsonDocument doc = QJsonDocument::fromVariant(formattedOutput);
jsonFile.write(doc.toJson(QJsonDocument::Indented));
jsonFile.close();
+
return qMakePair(true, jsonPath);
- } else {
+ }
+ else
+ {
return qMakePair(false, QString("Failed to write JSON File"));
}
- } catch (...) {
+ }
+ catch (...)
+ {
return qMakePair(false, QString("Error Saving Stub Hashes"));
}
}
-QString InlineAssemblyConverter::convertStubToInline(const QString& stubName, int syscallId) {
+QString InlineAssemblyConverter::convertStubToInline(const QString& stubName, int syscallId)
+{
QString syscallIdHex = QString("%1").arg(syscallId, 8, 16, QChar('0')).toUpper();
QString lowByte = syscallIdHex.mid(6, 2);
QString highByte = syscallIdHex.mid(4, 2);
+
QString inlineStub = QString("%1 PROC\n"
" ; mov r10, rcx\n"
" ; mov eax, %2h\n"
@@ -423,5 +637,6 @@ QString InlineAssemblyConverter::convertStubToInline(const QString& stubName, in
.arg(syscallId, 0, 16)
.arg(lowByte)
.arg(highByte);
+
return inlineStub;
}
diff --git a/Bind/src/GUI/Bars/SettingsTitleBar.cpp b/Bind/src/GUI/Bars/SettingsTitleBar.cpp
index 54bcb71..12fe4ce 100644
--- a/Bind/src/GUI/Bars/SettingsTitleBar.cpp
+++ b/Bind/src/GUI/Bars/SettingsTitleBar.cpp
@@ -5,34 +5,40 @@
#include
#include
-SettingsTitleBar::SettingsTitleBar(QWidget* parent) : QFrame(parent) {
+SettingsTitleBar::SettingsTitleBar(QWidget* parent)
+ : QFrame(parent)
+{
initTitleBar("Settings");
}
-SettingsTitleBar::SettingsTitleBar(const QString& title, QWidget* parent) : QFrame(parent) {
+SettingsTitleBar::SettingsTitleBar(const QString& title, QWidget* parent)
+ : QFrame(parent)
+{
initTitleBar(title);
}
-void SettingsTitleBar::initTitleBar(const QString& title) {
+void SettingsTitleBar::initTitleBar(const QString& title)
+{
setMaximumHeight(60);
- setStyleSheet(
- "QFrame {"
- " background: #252525;"
- " border-top-left-radius: 15px;"
- " border-top-right-radius: 15px;"
- "}"
- );
+ setStyleSheet("QFrame {"
+ " background: #252525;"
+ " border-top-left-radius: 15px;"
+ " border-top-right-radius: 15px;"
+ "}");
+
auto* layout = new QHBoxLayout(this);
layout->setContentsMargins(5, 0, 5, 0);
+
auto* titleLabel = new QLabel(title, this);
titleLabel->setStyleSheet("color: white; font-size: 16px; font-weight: bold;");
+
layout->addWidget(titleLabel);
layout->addStretch();
+
auto* closeBtn = new QToolButton(this);
- closeBtn->setStyleSheet(
- "QToolButton { background: #FF605C; border-radius: 7px; width: 14px; height: 14px; }"
- "QToolButton:hover { background: #FF8078; }"
- );
+ closeBtn->setStyleSheet("QToolButton { background: #FF605C; border-radius: 7px; width: 14px; height: 14px; }"
+ "QToolButton:hover { background: #FF8078; }");
+
connect(closeBtn, &QToolButton::clicked, this, &SettingsTitleBar::closeClicked);
layout->addWidget(closeBtn);
}
diff --git a/Bind/src/GUI/Bars/StatusBar.cpp b/Bind/src/GUI/Bars/StatusBar.cpp
index 45296f1..cbdb81f 100644
--- a/Bind/src/GUI/Bars/StatusBar.cpp
+++ b/Bind/src/GUI/Bars/StatusBar.cpp
@@ -2,53 +2,69 @@
#include
#include
-StatusBar::StatusBar(QWidget* parent) : QFrame(parent) {
+StatusBar::StatusBar(QWidget* parent)
+ : QFrame(parent)
+{
setMaximumHeight(40);
- setStyleSheet(
- "QFrame {"
- " background: #252525;"
- " border-bottom-left-radius: 15px;"
- " border-bottom-right-radius: 15px;"
- "}"
- );
+ setStyleSheet("QFrame {"
+ " background: #252525;"
+ " border-bottom-left-radius: 15px;"
+ " border-bottom-right-radius: 15px;"
+ "}");
+
auto* layout = new QHBoxLayout(this);
layout->setContentsMargins(20, 0, 20, 0);
+
statusIcon = new QLabel("⏺", this);
statusIcon->setStyleSheet("color: #666666; font-size: 16px;");
+
layout->addWidget(statusIcon);
+
statusMsg = new QLabel("Ready", this);
statusMsg->setStyleSheet("color: #666666; font-size: 12px;");
+
layout->addWidget(statusMsg);
layout->addStretch();
+
resultLabel = new QLabel(this);
- resultLabel->setStyleSheet(
- "QLabel {"
- " color: #666666;"
- " font-size: 12px;"
- " padding: 5px 10px;"
- " border-radius: 5px;"
- " background: rgba(37, 37, 37, 0.5);"
- "}"
- );
+ resultLabel->setStyleSheet("QLabel {"
+ " color: #666666;"
+ " font-size: 12px;"
+ " padding: 5px 10px;"
+ " border-radius: 5px;"
+ " background: rgba(37, 37, 37, 0.5);"
+ "}");
+
layout->addWidget(resultLabel);
}
-void StatusBar::updateStatus(const QString& message, const QString& statusType) {
+void StatusBar::updateStatus(const QString& message, const QString& statusType)
+{
statusMsg->setText(message);
+
QString icon, color;
- if (statusType == "working") {
+
+ if (statusType == "working")
+ {
icon = "⏳";
color = "#FFA500"; // orange
- } else if (statusType == "success") {
+ }
+ else if (statusType == "success")
+ {
icon = "✅";
color = "#00FF00"; // green
- } else if (statusType == "error") {
+ }
+ else if (statusType == "error")
+ {
icon = "❌";
color = "#FF0000"; // red
- } else {
+ }
+ else
+ {
icon = "⏺";
color = "#666666"; // gray
}
+
statusIcon->setText(icon);
statusIcon->setStyleSheet(QString("color: %1; font-size: 16px;").arg(color));
}
\ No newline at end of file
diff --git a/Bind/src/GUI/Bars/TitleBar.cpp b/Bind/src/GUI/Bars/TitleBar.cpp
index ac86b9f..24f6bf2 100644
--- a/Bind/src/GUI/Bars/TitleBar.cpp
+++ b/Bind/src/GUI/Bars/TitleBar.cpp
@@ -5,40 +5,45 @@
#include
#include
-TitleBar::TitleBar(QWidget* parent) : QFrame(parent) {
+TitleBar::TitleBar(QWidget* parent)
+ : QFrame(parent)
+{
setMaximumHeight(60);
- setStyleSheet(
- "QFrame {"
- " background: #252525;"
- " border-top-left-radius: 15px;"
- " border-top-right-radius: 15px;"
- "}"
- );
+ setStyleSheet("QFrame {"
+ " background: #252525;"
+ " border-top-left-radius: 15px;"
+ " border-top-right-radius: 15px;"
+ "}");
+
auto* layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 5, 0);
+
auto* title = new QLabel("", this);
title->setStyleSheet("color: white; font-size: 16px; font-weight: bold;");
+
layout->addWidget(title);
+
auto* controlsLayout = new QHBoxLayout();
controlsLayout->setSpacing(15);
+
auto* minimizeBtn = new QToolButton(this);
- minimizeBtn->setStyleSheet(
- "QToolButton { background: #FFB900; border-radius: 7px; width: 14px; height: 14px; }"
- "QToolButton:hover { background: #FFC933; }"
- );
+ minimizeBtn->setStyleSheet("QToolButton { background: #FFB900; border-radius: 7px; width: 14px; height: 14px; }"
+ "QToolButton:hover { background: #FFC933; }");
+
connect(minimizeBtn, &QToolButton::clicked, this, &TitleBar::minimizeClicked);
+
auto* maximizeBtn = new QToolButton(this);
- maximizeBtn->setStyleSheet(
- "QToolButton { background: #00CA4E; border-radius: 7px; width: 14px; height: 14px; }"
- "QToolButton:hover { background: #00E45B; }"
- );
+ maximizeBtn->setStyleSheet("QToolButton { background: #00CA4E; border-radius: 7px; width: 14px; height: 14px; }"
+ "QToolButton:hover { background: #00E45B; }");
+
connect(maximizeBtn, &QToolButton::clicked, this, &TitleBar::maximizeClicked);
+
auto* closeBtn = new QToolButton(this);
- closeBtn->setStyleSheet(
- "QToolButton { background: #FF605C; border-radius: 7px; width: 14px; height: 14px; }"
- "QToolButton:hover { background: #FF8078; }"
- );
+ closeBtn->setStyleSheet("QToolButton { background: #FF605C; border-radius: 7px; width: 14px; height: 14px; }"
+ "QToolButton:hover { background: #FF8078; }");
+
connect(closeBtn, &QToolButton::clicked, this, &TitleBar::closeClicked);
+
controlsLayout->addWidget(minimizeBtn);
controlsLayout->addWidget(maximizeBtn);
controlsLayout->addWidget(closeBtn);
diff --git a/Bind/src/GUI/Buttons/BindButton.cpp b/Bind/src/GUI/Buttons/BindButton.cpp
index f21b3ff..d54674c 100644
--- a/Bind/src/GUI/Buttons/BindButton.cpp
+++ b/Bind/src/GUI/Buttons/BindButton.cpp
@@ -3,10 +3,15 @@
#include
#include
-BindButton::BindButton(const QString& text, const QString& iconPath,
- const QString& title, const QString& description,
- QWidget* parent)
- : QPushButton(text, parent), title(title), description(description) {
+BindButton::BindButton(const QString& text,
+ const QString& iconPath,
+ const QString& title,
+ const QString& description,
+ QWidget* parent)
+ : QPushButton(text, parent)
+ , title(title)
+ , description(description)
+{
setIcon(QIcon(iconPath));
setIconSize(QSize(16, 16));
setMinimumHeight(40);
@@ -14,24 +19,23 @@ BindButton::BindButton(const QString& text, const QString& iconPath,
setupStyle();
}
-void BindButton::setupStyle() {
- setStyleSheet(
- "QPushButton {"
- " background: #0b5394;"
- " border: none;"
- " border-radius: 8px;"
- " padding: 10px 15px;"
- " color: white;"
- " font-family: 'IBM Plex Mono';"
- " font-size: 11px;"
- " font-weight: bold;"
- " text-align: left;"
- "}"
- "QPushButton:hover {"
- " background: #67abdb;"
- "}"
- "QPushButton:pressed {"
- " background: #0A7AD1;"
- "}"
- );
-}
\ No newline at end of file
+void BindButton::setupStyle()
+{
+ setStyleSheet("QPushButton {"
+ " background: #0b5394;"
+ " border: none;"
+ " border-radius: 8px;"
+ " padding: 10px 15px;"
+ " color: white;"
+ " font-family: 'IBM Plex Mono';"
+ " font-size: 11px;"
+ " font-weight: bold;"
+ " text-align: left;"
+ "}"
+ "QPushButton:hover {"
+ " background: #67abdb;"
+ "}"
+ "QPushButton:pressed {"
+ " background: #0A7AD1;"
+ "}");
+}
\ No newline at end of file
diff --git a/Bind/src/GUI/Dialogs/ChangelogDialog.cpp b/Bind/src/GUI/Dialogs/ChangelogDialog.cpp
index 8f1775a..0798363 100644
--- a/Bind/src/GUI/Dialogs/ChangelogDialog.cpp
+++ b/Bind/src/GUI/Dialogs/ChangelogDialog.cpp
@@ -16,7 +16,9 @@
#include
#include "include/Core/Utils/PathUtils.h"
-ChangelogDialog::ChangelogDialog(QWidget* parent) : QDialog(parent) {
+ChangelogDialog::ChangelogDialog(QWidget* parent)
+ : QDialog(parent)
+{
setWindowTitle("Bind - History");
setMinimumSize(1150, 600);
resize(1150, 600);
@@ -26,16 +28,22 @@ ChangelogDialog::ChangelogDialog(QWidget* parent) : QDialog(parent) {
setupStylesheet();
setupUI();
populateChangelogs();
- connect(listWidget, &QListWidget::currentItemChanged,
+
+ connect(listWidget, &QListWidget::currentItemChanged,
this, &ChangelogDialog::displayChangelog);
- if (listWidget->count() > 0) {
+
+ if (listWidget->count() > 0)
+ {
listWidget->setCurrentRow(0);
}
}
-void ChangelogDialog::setupStylesheet() {
+void ChangelogDialog::setupStylesheet()
+{
QFile stylesheetFile(":/src/GUI/Stylesheets/ChangelogDialog.qss");
- if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) {
+
+ if (stylesheetFile.open(QFile::ReadOnly | QFile::Text))
+ {
QTextStream in(&stylesheetFile);
QString stylesheet = in.readAll();
setStyleSheet(stylesheet);
@@ -43,84 +51,115 @@ void ChangelogDialog::setupStylesheet() {
}
}
-void ChangelogDialog::setupUI() {
+void ChangelogDialog::setupUI()
+{
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
+
titleBar = new SettingsTitleBar("Bind - Changelog History", this);
layout->addWidget(titleBar);
+
auto* contentLayout = new QVBoxLayout();
contentLayout->setContentsMargins(20, 20, 20, 20);
contentLayout->setSpacing(20);
+
auto* hbox = new QHBoxLayout();
listWidget = new QListWidget();
listWidget->setFixedWidth(200);
hbox->addWidget(listWidget);
+
textEdit = new QTextEdit();
textEdit->setReadOnly(true);
hbox->addWidget(textEdit, 1);
contentLayout->addLayout(hbox);
+
auto* btnBox = new QHBoxLayout();
btnBox->addStretch();
+
auto* closeBtn = new QPushButton("Close");
connect(closeBtn, &QPushButton::clicked, this, &QDialog::accept);
btnBox->addWidget(closeBtn);
contentLayout->addLayout(btnBox);
layout->addLayout(contentLayout);
+
connect(titleBar, &SettingsTitleBar::closeClicked, this, &QDialog::accept);
}
-void ChangelogDialog::populateChangelogs() {
+void ChangelogDialog::populateChangelogs()
+{
QString historyDir = PathUtils::getProjectRoot() + "/History";
QDir dir(historyDir);
QStringList changelogs;
QStringList filters;
filters << "CHANGELOG_*.md";
+
QFileInfoList files = dir.entryInfoList(filters, QDir::Files);
- for (const QFileInfo& fileInfo : files) {
+
+ for (const QFileInfo& fileInfo : files)
+ {
QString fileName = fileInfo.fileName();
- if (fileName.startsWith("CHANGELOG_") && fileName.endsWith(".md")) {
+
+ if (fileName.startsWith("CHANGELOG_") && fileName.endsWith(".md"))
+ {
QString version = fileName.mid(10, fileName.length() - 13);
changelogFiles[version] = fileInfo.absoluteFilePath();
changelogs.append(version);
}
}
+
std::sort(changelogs.begin(), changelogs.end(), std::greater());
- for (const QString& version : changelogs) {
+
+ for (const QString& version : changelogs)
+ {
listWidget->addItem(version);
}
}
-void ChangelogDialog::displayChangelog(QListWidgetItem* current, QListWidgetItem* previous) {
+void ChangelogDialog::displayChangelog(QListWidgetItem* current, QListWidgetItem* previous)
+{
Q_UNUSED(previous)
- if (!current) {
+
+ if (!current)
+ {
textEdit->clear();
return;
}
+
QString version = current->text();
QString filePath = changelogFiles.value(version);
- if (!filePath.isEmpty() && QFile::exists(filePath)) {
+
+ if (!filePath.isEmpty() && QFile::exists(filePath))
+ {
QFile file(filePath);
- if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
QTextStream stream(&file);
stream.setCodec("UTF-8");
QString content = stream.readAll();
file.close();
QString html = markdownToHtml(content);
textEdit->setHtml(html);
- } else {
+ }
+ else
+ {
textEdit->setHtml("[Error Reading Changelog File]");
}
- } else {
+ }
+ else
+ {
textEdit->setHtml("[No Changelog Found]");
}
}
-QString ChangelogDialog::markdownToHtml(const QString& markdown) {
+QString ChangelogDialog::markdownToHtml(const QString& markdown)
+{
QByteArray utf8 = markdown.toUtf8();
char* html = cmark_markdown_to_html(utf8.constData(), utf8.size(), CMARK_OPT_DEFAULT);
QString result = QString::fromUtf8(html);
free(html);
+
QString customCss =
"";
+
return customCss + result;
}
-void ChangelogDialog::mousePressEvent(QMouseEvent* event) {
- if (event->button() == Qt::LeftButton) {
+void ChangelogDialog::mousePressEvent(QMouseEvent* event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
m_dragging = true;
m_dragPosition = event->globalPos() - frameGeometry().topLeft();
event->accept();
}
}
-void ChangelogDialog::mouseMoveEvent(QMouseEvent* event) {
- if (event->buttons() & Qt::LeftButton && m_dragging) {
+void ChangelogDialog::mouseMoveEvent(QMouseEvent* event)
+{
+ if (event->buttons() & Qt::LeftButton && m_dragging)
+ {
move(event->globalPos() - m_dragPosition);
event->accept();
}
}
-void ChangelogDialog::mouseReleaseEvent(QMouseEvent* event) {
- if (event->button() == Qt::LeftButton) {
+void ChangelogDialog::mouseReleaseEvent(QMouseEvent* event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
m_dragging = false;
event->accept();
}
diff --git a/Bind/src/GUI/Dialogs/ConfirmationDialog.cpp b/Bind/src/GUI/Dialogs/ConfirmationDialog.cpp
index b10d2c1..5cc46e8 100644
--- a/Bind/src/GUI/Dialogs/ConfirmationDialog.cpp
+++ b/Bind/src/GUI/Dialogs/ConfirmationDialog.cpp
@@ -4,7 +4,9 @@
#include
#include
-ConfirmationDialog::ConfirmationDialog(QWidget* parent) : QDialog(parent) {
+ConfirmationDialog::ConfirmationDialog(QWidget* parent)
+ : QDialog(parent)
+{
setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
setMinimumSize(405, 205);
@@ -12,7 +14,9 @@ ConfirmationDialog::ConfirmationDialog(QWidget* parent) : QDialog(parent) {
setupStylesheet();
}
-ConfirmationDialog::ConfirmationDialog(const QString& title, QWidget* parent) : QDialog(parent) {
+ConfirmationDialog::ConfirmationDialog(const QString& title, QWidget* parent)
+ : QDialog(parent)
+{
setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
setMinimumSize(405, 205);
@@ -22,24 +26,36 @@ ConfirmationDialog::ConfirmationDialog(const QString& title, QWidget* parent) :
ConfirmationDialog::~ConfirmationDialog() = default;
-void ConfirmationDialog::setTitle(const QString& title) {
+void ConfirmationDialog::setTitle(const QString& title)
+{
Q_UNUSED(title)
}
-void ConfirmationDialog::setMessage(const QString& message) {
- if (messageLabel) {
+void ConfirmationDialog::setMessage(const QString& message)
+{
+ if (messageLabel)
+ {
messageLabel->setText(message);
}
}
-void ConfirmationDialog::setButtons(bool showYes, bool showNo, bool showOK, bool showCancel) {
- if (yesButton) yesButton->setVisible(showYes);
- if (noButton) noButton->setVisible(showNo);
- if (okButton) okButton->setVisible(showOK);
- if (cancelButton) cancelButton->setVisible(showCancel);
+void ConfirmationDialog::setButtons(bool showYes, bool showNo, bool showOK, bool showCancel)
+{
+ if (yesButton)
+ yesButton->setVisible(showYes);
+
+ if (noButton)
+ noButton->setVisible(showNo);
+
+ if (okButton)
+ okButton->setVisible(showOK);
+
+ if (cancelButton)
+ cancelButton->setVisible(showCancel);
}
-void ConfirmationDialog::initUI(const QString& title) {
+void ConfirmationDialog::initUI(const QString& title)
+{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
@@ -88,14 +104,19 @@ void ConfirmationDialog::initUI(const QString& title) {
layout->addWidget(contentWidget);
}
-void ConfirmationDialog::setupStylesheet() {
+void ConfirmationDialog::setupStylesheet()
+{
QFile stylesheetFile(":/src/GUI/Stylesheets/ConfirmationDialog.qss");
- if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) {
+
+ if (stylesheetFile.open(QFile::ReadOnly | QFile::Text))
+ {
QTextStream in(&stylesheetFile);
QString stylesheet = in.readAll();
setStyleSheet(stylesheet);
stylesheetFile.close();
- } else {
+ }
+ else
+ {
setStyleSheet(
"QDialog { background: #252525; color: white; border-radius: 15px; }"
"QPushButton { background: #0b5394; border: none; border-radius: 5px; padding: 8px 15px; color: white; font-weight: bold; }"
@@ -106,43 +127,53 @@ void ConfirmationDialog::setupStylesheet() {
}
}
-void ConfirmationDialog::onYesClicked() {
+void ConfirmationDialog::onYesClicked()
+{
result = Yes;
accept();
}
-void ConfirmationDialog::onNoClicked() {
+void ConfirmationDialog::onNoClicked()
+{
result = No;
reject();
}
-void ConfirmationDialog::onOKClicked() {
+void ConfirmationDialog::onOKClicked()
+{
result = OK;
accept();
}
-void ConfirmationDialog::onCancelClicked() {
+void ConfirmationDialog::onCancelClicked()
+{
result = Cancel;
reject();
}
-void ConfirmationDialog::mousePressEvent(QMouseEvent* event) {
- if (event->button() == Qt::LeftButton) {
+void ConfirmationDialog::mousePressEvent(QMouseEvent* event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
m_dragging = true;
m_dragPosition = event->globalPos() - frameGeometry().topLeft();
event->accept();
}
}
-void ConfirmationDialog::mouseMoveEvent(QMouseEvent* event) {
- if (event->buttons() & Qt::LeftButton && m_dragging) {
+void ConfirmationDialog::mouseMoveEvent(QMouseEvent* event)
+{
+ if (event->buttons() & Qt::LeftButton && m_dragging)
+ {
move(event->globalPos() - m_dragPosition);
event->accept();
}
}
-void ConfirmationDialog::mouseReleaseEvent(QMouseEvent* event) {
- if (event->button() == Qt::LeftButton) {
+void ConfirmationDialog::mouseReleaseEvent(QMouseEvent* event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
m_dragging = false;
event->accept();
}
diff --git a/Bind/src/GUI/Dialogs/HashCompareDialog.cpp b/Bind/src/GUI/Dialogs/HashCompareDialog.cpp
index fadc8e1..8826c10 100644
--- a/Bind/src/GUI/Dialogs/HashCompareDialog.cpp
+++ b/Bind/src/GUI/Dialogs/HashCompareDialog.cpp
@@ -29,7 +29,10 @@
#include
#include
-HashCompareDialog::HashCompareDialog(QWidget* parent) : QDialog(parent), hashType("MD5") {
+HashCompareDialog::HashCompareDialog(QWidget* parent)
+ : QDialog(parent)
+ , hashType("MD5")
+{
setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
setMinimumSize(950, 400);
titleBar = new SettingsTitleBar("Hash Compare", this);
@@ -38,22 +41,28 @@ HashCompareDialog::HashCompareDialog(QWidget* parent) : QDialog(parent), hashTyp
loadHashFiles();
}
-void HashCompareDialog::initUI() {
+void HashCompareDialog::initUI()
+{
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addWidget(titleBar);
+
auto* topLayout = new QHBoxLayout();
topLayout->setContentsMargins(20, 10, 20, 10);
+
refreshBtn = new QPushButton("Refresh");
- refreshBtn->setIcon(QIcon(":/src/Res/Icons/refresh.svg"));
+ refreshBtn->setIcon(QIcon(":/src/Res/Icons/refresh.png"));
connect(refreshBtn, &QPushButton::clicked, this, &HashCompareDialog::loadHashFiles);
topLayout->addWidget(refreshBtn);
+
topLayout->addSpacing(20);
+
auto* hashTypeLayout = new QHBoxLayout();
auto* hashTypeLabel = new QLabel("Hash Type:");
hashTypeLabel->setStyleSheet("color: white; background: transparent;");
hashTypeLayout->addWidget(hashTypeLabel);
+
hashTypeCombo = new QComboBox();
hashTypeCombo->addItems({"MD5", "SHA-256"});
hashTypeCombo->setCurrentText(hashType);
@@ -61,33 +70,42 @@ void HashCompareDialog::initUI() {
this, &HashCompareDialog::onHashTypeChanged);
hashTypeLayout->addWidget(hashTypeCombo);
topLayout->addLayout(hashTypeLayout);
+
topLayout->addStretch();
+
exportBtn = new QPushButton("Export Comparison");
- exportBtn->setIcon(QIcon(":/src/Res/Icons/export.svg"));
+ exportBtn->setIcon(QIcon(":/src/Res/Icons/export.png"));
connect(exportBtn, &QPushButton::clicked, this, &HashCompareDialog::exportComparison);
exportBtn->setEnabled(false);
topLayout->addWidget(exportBtn);
layout->addLayout(topLayout);
+
splitter = new QSplitter(Qt::Horizontal);
+
// left side, hash file list
auto* leftPanel = new QGroupBox("Hash Files");
auto* leftLayout = new QVBoxLayout(leftPanel);
+
showOnlyDifferences = new QCheckBox("Highlight Duplicates");
showOnlyDifferences->setChecked(true);
connect(showOnlyDifferences, &QCheckBox::stateChanged, this, &HashCompareDialog::updateHashTable);
leftLayout->addWidget(showOnlyDifferences);
+
hashFileList = new QListWidget();
hashFileList->setSelectionMode(QAbstractItemView::ExtendedSelection);
- connect(hashFileList, &QListWidget::itemSelectionChanged,
+ connect(hashFileList, &QListWidget::itemSelectionChanged,
this, &HashCompareDialog::selectionChanged);
leftLayout->addWidget(hashFileList);
+
compareBtn = new QPushButton("Compare Selected");
connect(compareBtn, &QPushButton::clicked, this, &HashCompareDialog::compareSelected);
leftLayout->addWidget(compareBtn);
splitter->addWidget(leftPanel);
+
// right side, hash table
auto* rightPanel = new QGroupBox("Hash Comparison");
auto* rightLayout = new QVBoxLayout(rightPanel);
+
hashTable = new QTableWidget(0, 3);
hashTable->setHorizontalHeaderLabels({"Syscall", "Hash File 1", "Hash File 2"});
hashTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
@@ -100,18 +118,24 @@ void HashCompareDialog::initUI() {
splitter->addWidget(rightPanel);
layout->addWidget(splitter);
splitter->setSizes({300, 600});
+
auto* buttonLayout = new QHBoxLayout();
buttonLayout->addStretch();
+
auto* closeBtn = new QPushButton("Close");
connect(closeBtn, &QPushButton::clicked, this, &QDialog::reject);
buttonLayout->addWidget(closeBtn);
layout->addLayout(buttonLayout);
+
connect(titleBar, &SettingsTitleBar::closeClicked, this, &QDialog::reject);
}
-void HashCompareDialog::setupStylesheet() {
+void HashCompareDialog::setupStylesheet()
+{
QFile stylesheetFile(":/src/GUI/Stylesheets/HashCompareDialog.qss");
- if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) {
+
+ if (stylesheetFile.open(QFile::ReadOnly | QFile::Text))
+ {
QTextStream in(&stylesheetFile);
QString stylesheet = in.readAll();
setStyleSheet(stylesheet);
@@ -119,206 +143,311 @@ void HashCompareDialog::setupStylesheet() {
}
}
-void HashCompareDialog::mousePressEvent(QMouseEvent* event) {
- if (event->button() == Qt::LeftButton) {
+void HashCompareDialog::mousePressEvent(QMouseEvent* event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
m_dragging = true;
m_dragPosition = event->globalPos() - frameGeometry().topLeft();
event->accept();
}
}
-void HashCompareDialog::mouseMoveEvent(QMouseEvent* event) {
- if (event->buttons() & Qt::LeftButton && m_dragging) {
+void HashCompareDialog::mouseMoveEvent(QMouseEvent* event)
+{
+ if (event->buttons() & Qt::LeftButton && m_dragging)
+ {
move(event->globalPos() - m_dragPosition);
event->accept();
}
}
-void HashCompareDialog::mouseReleaseEvent(QMouseEvent* event) {
- if (event->button() == Qt::LeftButton) {
+void HashCompareDialog::mouseReleaseEvent(QMouseEvent* event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
m_dragging = false;
event->accept();
}
}
-void HashCompareDialog::loadHashFiles() {
- try {
+void HashCompareDialog::loadHashFiles()
+{
+ try
+ {
QString hashBackupsDir = PathUtils::getHashBackupsPath();
QDir dir(hashBackupsDir);
- if (!dir.exists()) {
+
+ if (!dir.exists())
+ {
hashFileList->clear();
hashFileList->addItem("No Hash Directory Found");
return;
}
+
hashFileList->clear();
hashFiles.clear();
hashData.clear();
+
QStringList filters;
filters << "stub_hashes_*.json";
QFileInfoList files = dir.entryInfoList(filters, QDir::Files);
+
// sort files in reverse order (newest first)
- std::sort(files.begin(), files.end(), [](const QFileInfo& a, const QFileInfo& b) {
+ std::sort(files.begin(), files.end(), [](const QFileInfo& a, const QFileInfo& b)
+ {
return a.fileName() > b.fileName();
});
- for (const QFileInfo& fileInfo : files) {
- try {
+
+ for (const QFileInfo& fileInfo : files)
+ {
+ try
+ {
QString filePath = fileInfo.absoluteFilePath();
QFile file(filePath);
- if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
QTextStream stream(&file);
stream.setCodec("UTF-8");
QString content = stream.readAll();
file.close();
+
QJsonDocument doc = QJsonDocument::fromJson(content.toUtf8());
- if (doc.isObject()) {
+
+ if (doc.isObject())
+ {
QJsonObject jsonObj = doc.object();
QVariantMap data;
+
data["filepath"] = filePath;
data["content"] = content;
data["json"] = jsonObj.toVariantMap();
+
QString timestamp = jsonObj["timestamp"].toString();
- if (timestamp.isEmpty()) timestamp = "Unknown";
+
+ if (timestamp.isEmpty())
+ {
+ timestamp = "Unknown";
+ }
+
QString obfMethod = "Normal";
QJsonObject config = jsonObj["config"].toObject();
- if (!config.isEmpty()) {
- if (config.contains("obfuscation_method")) {
+
+ if (!config.isEmpty())
+ {
+ if (config.contains("obfuscation_method"))
+ {
obfMethod = config["obfuscation_method"].toString();
- } else if (config.contains("global_settings")) {
+ }
+ else if (config.contains("global_settings"))
+ {
obfMethod = "Stub Mapper";
}
}
+
QString displayName = QString("%1 (%2)").arg(timestamp, obfMethod);
hashFileList->addItem(displayName);
hashFiles.append(filePath);
hashData[filePath] = data;
}
}
- } catch (...) {
+ }
+ catch (...)
+ {
continue;
}
}
- if (hashFiles.isEmpty()) {
+
+ if (hashFiles.isEmpty())
+ {
hashFileList->addItem("No Hash Files Found");
}
- } catch (...) {
+ }
+ catch (...)
+ {
hashFileList->clear();
hashFileList->addItem("Error Loading Hash Files");
}
}
-void HashCompareDialog::selectionChanged() {
+void HashCompareDialog::selectionChanged()
+{
QList selectedItems = hashFileList->selectedItems();
exportBtn->setEnabled(selectedItems.size() >= 1);
}
-void HashCompareDialog::onHashTypeChanged(const QString& hashType) {
+void HashCompareDialog::onHashTypeChanged(const QString& hashType)
+{
this->hashType = hashType;
updateHashTable();
}
-void HashCompareDialog::updateHashTable() {
+void HashCompareDialog::updateHashTable()
+{
QList selectedItems = hashFileList->selectedItems();
- if (selectedItems.isEmpty()) {
+
+ if (selectedItems.isEmpty())
+ {
return;
}
+
QStringList selectedFiles;
- for (QListWidgetItem* item : selectedItems) {
+
+ for (QListWidgetItem* item : selectedItems)
+ {
int index = hashFileList->row(item);
- if (index >= 0 && index < hashFiles.size()) {
+
+ if (index >= 0 && index < hashFiles.size())
+ {
selectedFiles.append(hashFiles[index]);
}
}
- if (!selectedFiles.isEmpty()) {
+
+ if (!selectedFiles.isEmpty())
+ {
displayComparison(selectedFiles);
}
}
-void HashCompareDialog::compareSelected() {
+void HashCompareDialog::compareSelected()
+{
QList selectedItems = hashFileList->selectedItems();
- if (selectedItems.size() < 1) {
- QMessageBox::warning(this, "Bind - v1.3.0", "Please select at least one Hash File to view.");
+
+ if (selectedItems.size() < 1)
+ {
+ QMessageBox::warning(this, "Bind - v1.3.1", "Please select at least one Hash File to view.");
return;
}
- if (selectedItems.size() > 5) {
- QMessageBox::warning(this, "Bind - v1.3.0", "Please select at most 5 Hash Files to compare.");
+
+ if (selectedItems.size() > 5)
+ {
+ QMessageBox::warning(this, "Bind - v1.3.1", "Please select at most 5 Hash Files to compare.");
return;
}
+
QStringList selectedFiles;
- for (QListWidgetItem* item : selectedItems) {
+
+ for (QListWidgetItem* item : selectedItems)
+ {
int index = hashFileList->row(item);
- if (index >= 0 && index < hashFiles.size()) {
+
+ if (index >= 0 && index < hashFiles.size())
+ {
selectedFiles.append(hashFiles[index]);
}
}
+
displayComparison(selectedFiles);
}
-void HashCompareDialog::displayComparison(const QStringList& files) {
- if (files.isEmpty()) {
+void HashCompareDialog::displayComparison(const QStringList& files)
+{
+ if (files.isEmpty())
+ {
return;
}
+
hashTable->clear();
hashTable->setSortingEnabled(false);
hashTable->setColumnCount(files.size() + 1);
+
QStringList headers;
headers << "Syscall";
+
QSet allSyscalls;
QMap fileData;
- for (const QString& filePath : files) {
+
+ for (const QString& filePath : files)
+ {
QVariantMap data = hashData.value(filePath);
QVariantMap jsonData = data["json"].toMap();
QString timestamp = jsonData["timestamp"].toString();
- if (timestamp.isEmpty()) timestamp = "Unknown";
+
+ if (timestamp.isEmpty())
+ {
+ timestamp = "Unknown";
+ }
+
QString obfMethod = "Normal";
QVariantMap config = jsonData["config"].toMap();
- if (!config.isEmpty()) {
- if (config.contains("obfuscation_method")) {
+
+ if (!config.isEmpty())
+ {
+ if (config.contains("obfuscation_method"))
+ {
obfMethod = config["obfuscation_method"].toString();
- } else if (config.contains("global_settings")) {
+ }
+ else if (config.contains("global_settings"))
+ {
obfMethod = "Stub Mapper";
}
}
+
headers << QString("%1\n(%2)").arg(timestamp, obfMethod);
fileData[filePath] = jsonData;
+
QVariantMap stubs = jsonData["stubs"].toMap();
- for (auto it = stubs.begin(); it != stubs.end(); ++it) {
+
+ for (auto it = stubs.begin(); it != stubs.end(); ++it)
+ {
allSyscalls.insert(it.key());
}
}
+
hashTable->setHorizontalHeaderLabels(headers);
+
// create hash mapping for duplicate detection
QMap>> hashMapping;
QStringList sortedSyscalls = allSyscalls.values();
std::sort(sortedSyscalls.begin(), sortedSyscalls.end());
hashTable->setRowCount(sortedSyscalls.size());
- for (int row = 0; row < sortedSyscalls.size(); ++row) {
+
+ for (int row = 0; row < sortedSyscalls.size(); ++row)
+ {
QString syscall = sortedSyscalls[row];
hashTable->setItem(row, 0, new QTableWidgetItem(syscall));
- for (int col = 0; col < files.size(); ++col) {
+
+ for (int col = 0; col < files.size(); ++col)
+ {
QString filePath = files[col];
QVariantMap jsonData = fileData[filePath];
QVariantMap stubs = jsonData["stubs"].toMap();
QString hashValue = stubs.value(syscall).toString();
QString displayValue = "N/A";
- if (!hashValue.isEmpty()) {
+
+ if (!hashValue.isEmpty())
+ {
QString extractedHash = extractHash(hashValue, hashType);
- if (extractedHash != "N/A") {
+
+ if (extractedHash != "N/A")
+ {
displayValue = extractedHash;
- if (!hashMapping.contains(extractedHash)) {
+
+ if (!hashMapping.contains(extractedHash))
+ {
hashMapping[extractedHash] = QList>();
}
+
hashMapping[extractedHash].append(qMakePair(syscall, col + 1));
}
}
+
QTableWidgetItem* item = new QTableWidgetItem(displayValue);
- if (hashValue.isEmpty()) {
+
+ if (hashValue.isEmpty())
+ {
item->setBackground(QColor(80, 80, 80));
}
+
hashTable->setItem(row, col + 1, item);
}
}
- if (showOnlyDifferences->isChecked()) {
- QList duplicateColors = {
+
+ if (showOnlyDifferences->isChecked())
+ {
+ QList duplicateColors =
+ {
QColor(255, 150, 150), // red
QColor(150, 255, 150), // green
QColor(150, 150, 255), // blue
@@ -327,16 +456,26 @@ void HashCompareDialog::displayComparison(const QStringList& files) {
QColor(150, 255, 255), // cyan
QColor(255, 200, 150), // orange
};
+
int colorIndex = 0;
- for (auto it = hashMapping.begin(); it != hashMapping.end(); ++it) {
- if (it.value().size() > 1) {
+
+ for (auto it = hashMapping.begin(); it != hashMapping.end(); ++it)
+ {
+ if (it.value().size() > 1)
+ {
QColor color = duplicateColors[colorIndex % duplicateColors.size()];
colorIndex++;
- for (const auto& pos : it.value()) {
+
+ for (const auto& pos : it.value())
+ {
int row = sortedSyscalls.indexOf(pos.first);
- if (row >= 0) {
+
+ if (row >= 0)
+ {
QTableWidgetItem* item = hashTable->item(row, pos.second);
- if (item && row % 2 == 1) {
+
+ if (item && row % 2 == 1)
+ {
item->setBackground(color);
}
}
@@ -344,151 +483,238 @@ void HashCompareDialog::displayComparison(const QStringList& files) {
}
}
}
- for (int i = 0; i < hashTable->columnCount(); ++i) {
+
+ for (int i = 0; i < hashTable->columnCount(); ++i)
+ {
hashTable->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Stretch);
}
+
hashTable->setSortingEnabled(true);
exportBtn->setEnabled(true);
}
-QString HashCompareDialog::extractHash(const QString& hashValue, const QString& hashType) {
- if (hashType == "MD5") {
+QString HashCompareDialog::extractHash(const QString& hashValue, const QString& hashType)
+{
+ if (hashType == "MD5")
+ {
QRegExp md5Regex("MD5:\\s*([a-fA-F0-9]{32})");
- if (md5Regex.indexIn(hashValue) != -1) {
+
+ if (md5Regex.indexIn(hashValue) != -1)
+ {
return md5Regex.cap(1);
}
- } else if (hashType == "SHA-256") {
+ }
+ else if (hashType == "SHA-256")
+ {
QRegExp sha256Regex("SHA-256:\\s*([a-fA-F0-9]{64})");
- if (sha256Regex.indexIn(hashValue) != -1) {
+
+ if (sha256Regex.indexIn(hashValue) != -1)
+ {
return sha256Regex.cap(1);
}
}
+
return hashValue;
}
-void HashCompareDialog::exportComparison() {
+void HashCompareDialog::exportComparison()
+{
QList selectedItems = hashFileList->selectedItems();
- if (selectedItems.isEmpty()) {
- QMessageBox::warning(this, "Bind - v1.3.0", "Please select at least one Hash File to export.");
+
+ if (selectedItems.isEmpty())
+ {
+ QMessageBox::warning(this, "Bind - v1.3.1", "Please select at least one Hash File to export.");
return;
}
+
QStringList selectedFiles;
- for (QListWidgetItem* item : selectedItems) {
+
+ for (QListWidgetItem* item : selectedItems)
+ {
int index = hashFileList->row(item);
- if (index >= 0 && index < hashFiles.size()) {
+
+ if (index >= 0 && index < hashFiles.size())
+ {
selectedFiles.append(hashFiles[index]);
}
}
+
QString exportPath = QFileDialog::getSaveFileName(
this,
- "Bind - v1.3.0",
+ "Bind - v1.3.1",
"",
"CSV Files (*.csv);;HTML Files (*.html);;All Files (*.*)"
);
- if (exportPath.isEmpty()) {
+
+ if (exportPath.isEmpty())
+ {
return;
}
- try {
- if (exportPath.toLower().endsWith(".csv")) {
+
+ try
+ {
+ if (exportPath.toLower().endsWith(".csv"))
+ {
exportAsCsv(exportPath, selectedFiles);
- } else if (exportPath.toLower().endsWith(".html")) {
+ }
+ else if (exportPath.toLower().endsWith(".html"))
+ {
exportAsHtml(exportPath, selectedFiles);
- } else {
- if (!exportPath.toLower().endsWith(".csv")) {
+ }
+ else
+ {
+ if (!exportPath.toLower().endsWith(".csv"))
+ {
exportPath += ".csv";
}
+
exportAsCsv(exportPath, selectedFiles);
}
- QMessageBox::information(this, "Bind - v1.3.0",
+
+ QMessageBox::information(this, "Bind - v1.3.1",
QString("Hash Comparison exported successfully to:\n%1").arg(exportPath));
- } catch (...) {
- QMessageBox::critical(this, "Bind - v1.3.0", "Failed to Export Comparison.");
+ }
+ catch (...)
+ {
+ QMessageBox::critical(this, "Bind - v1.3.1", "Failed to Export Comparison.");
}
}
-void HashCompareDialog::exportAsCsv(const QString& exportPath, const QStringList& selectedFiles) {
- if (selectedFiles.isEmpty()) {
+void HashCompareDialog::exportAsCsv(const QString& exportPath, const QStringList& selectedFiles)
+{
+ if (selectedFiles.isEmpty())
+ {
return;
}
+
QSet allSyscalls;
QMap fileData;
- for (const QString& filePath : selectedFiles) {
+
+ for (const QString& filePath : selectedFiles)
+ {
QVariantMap data = hashData.value(filePath);
QVariantMap jsonData = data["json"].toMap();
fileData[filePath] = jsonData;
+
QVariantMap stubs = jsonData["stubs"].toMap();
- for (auto it = stubs.begin(); it != stubs.end(); ++it) {
+
+ for (auto it = stubs.begin(); it != stubs.end(); ++it)
+ {
allSyscalls.insert(it.key());
}
}
+
QFile file(exportPath);
- if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QMessageBox::critical(this, "Bind - v1.3.0", "Could not create Export File.");
+
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
+ QMessageBox::critical(this, "Bind - v1.3.1", "Could not create Export File.");
return;
}
+
QTextStream stream(&file);
stream.setCodec("UTF-8");
+
stream << "Syscall";
- for (const QString& filePath : selectedFiles) {
+
+ for (const QString& filePath : selectedFiles)
+ {
QVariantMap jsonData = fileData[filePath];
QString timestamp = jsonData["timestamp"].toString();
- if (timestamp.isEmpty()) timestamp = "Unknown";
+
+ if (timestamp.isEmpty())
+ {
+ timestamp = "Unknown";
+ }
+
QString obfMethod = "Normal";
QVariantMap config = jsonData["config"].toMap();
- if (!config.isEmpty()) {
- if (config.contains("obfuscation_method")) {
+
+ if (!config.isEmpty())
+ {
+ if (config.contains("obfuscation_method"))
+ {
obfMethod = config["obfuscation_method"].toString();
- } else if (config.contains("global_settings")) {
+ }
+ else if (config.contains("global_settings"))
+ {
obfMethod = "Stub Mapper";
}
}
+
stream << ",\"" << timestamp << " (" << obfMethod << ")\"";
}
+
stream << "\n";
+
QStringList sortedSyscalls = allSyscalls.values();
std::sort(sortedSyscalls.begin(), sortedSyscalls.end());
- for (const QString& syscall : sortedSyscalls) {
+
+ for (const QString& syscall : sortedSyscalls)
+ {
stream << "\"" << syscall << "\"";
- for (const QString& filePath : selectedFiles) {
+
+ for (const QString& filePath : selectedFiles)
+ {
QVariantMap jsonData = fileData[filePath];
QVariantMap stubs = jsonData["stubs"].toMap();
QString hashValue = stubs.value(syscall).toString();
QString displayValue = "N/A";
- if (!hashValue.isEmpty()) {
+
+ if (!hashValue.isEmpty())
+ {
QString extractedHash = extractHash(hashValue, hashType);
- if (extractedHash != "N/A") {
+
+ if (extractedHash != "N/A")
+ {
displayValue = extractedHash;
}
}
+
stream << ",\"" << displayValue << "\"";
}
+
stream << "\n";
}
+
file.close();
}
-void HashCompareDialog::exportAsHtml(const QString& exportPath, const QStringList& selectedFiles) {
- if (selectedFiles.isEmpty()) {
+void HashCompareDialog::exportAsHtml(const QString& exportPath, const QStringList& selectedFiles)
+{
+ if (selectedFiles.isEmpty())
+ {
return;
}
+
QSet allSyscalls;
QMap fileData;
- for (const QString& filePath : selectedFiles) {
+
+ for (const QString& filePath : selectedFiles)
+ {
QVariantMap data = hashData.value(filePath);
QVariantMap jsonData = data["json"].toMap();
fileData[filePath] = jsonData;
+
QVariantMap stubs = jsonData["stubs"].toMap();
- for (auto it = stubs.begin(); it != stubs.end(); ++it) {
+
+ for (auto it = stubs.begin(); it != stubs.end(); ++it)
+ {
allSyscalls.insert(it.key());
}
}
+
QFile file(exportPath);
- if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QMessageBox::critical(this, "Bind - v1.3.0", "Could not create Export File.");
+
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
+ QMessageBox::critical(this, "Bind - v1.3.1", "Could not create Export File.");
return;
}
+
QTextStream stream(&file);
stream.setCodec("UTF-8");
+
stream << "\n";
stream << "\n\n";
stream << "