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 << "\n"; @@ -508,73 +734,120 @@ void HashCompareDialog::exportAsHtml(const QString& exportPath, const QStringLis stream << ".hash-type { font-weight: bold; color: #0b5394; }\n"; stream << "\n\n\n"; stream << "

Bind - Hash Comparison

\n"; + QString firstTimestamp = fileData.value(selectedFiles.first())["timestamp"].toString(); - if (firstTimestamp.isEmpty()) firstTimestamp = "Unknown"; + + if (firstTimestamp.isEmpty()) + { + firstTimestamp = "Unknown"; + } + stream << "

Generated on: " << firstTimestamp << "

\n"; stream << "

Hash Type: " << hashType << "

\n"; stream << "\n"; - 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 << ""; } + stream << "\n"; + QMap>> hashMapping; - for (const QString& syscall : allSyscalls) { - for (const QString& filePath : selectedFiles) { + + for (const QString& syscall : allSyscalls) + { + for (const QString& filePath : selectedFiles) + { QVariantMap jsonData = fileData[filePath]; QVariantMap stubs = jsonData["stubs"].toMap(); QString hashValue = stubs.value(syscall).toString(); - if (!hashValue.isEmpty()) { + + if (!hashValue.isEmpty()) + { QString extractedHash = extractHash(hashValue, hashType); - if (extractedHash != "N/A") { - if (!hashMapping.contains(extractedHash)) { + + if (extractedHash != "N/A") + { + if (!hashMapping.contains(extractedHash)) + { hashMapping[extractedHash] = QList>(); } + hashMapping[extractedHash].append(qMakePair(syscall, filePath)); } } } } + QStringList sortedSyscalls = allSyscalls.values(); std::sort(sortedSyscalls.begin(), sortedSyscalls.end()); - for (const QString& syscall : sortedSyscalls) { + + for (const QString& syscall : sortedSyscalls) + { stream << ""; - 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; } } + QString duplicateClass = ""; - if (displayValue != "N/A") { - if (hashMapping.contains(displayValue) && hashMapping[displayValue].size() > 1) { + + if (displayValue != "N/A") + { + if (hashMapping.contains(displayValue) && hashMapping[displayValue].size() > 1) + { duplicateClass = " class=\"duplicate\""; } } + stream << "" << displayValue << ""; } + stream << "\n"; } + stream << "
Syscall" << timestamp << "
(" << obfMethod << ")
" << syscall << "
\n\n"; file.close(); } -QString HashCompareDialog::getProjectPaths() { +QString HashCompareDialog::getProjectPaths() +{ return PathUtils::getProjectRoot(); } diff --git a/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp b/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp index e414a34..5c98576 100644 --- a/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp +++ b/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp @@ -13,7 +13,7 @@ ObfuscationSelectionDialog::ObfuscationSelectionDialog(QWidget* parent) : QDialog(parent) , selection(Cancelled) { - setWindowTitle("Bind - v1.3.0"); + setWindowTitle("Bind - v1.3.1"); setFixedSize(450, 300); setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground); @@ -21,9 +21,12 @@ ObfuscationSelectionDialog::ObfuscationSelectionDialog(QWidget* parent) initUI(); } -void ObfuscationSelectionDialog::setupStylesheet() { +void ObfuscationSelectionDialog::setupStylesheet() +{ QFile stylesheetFile(":/src/GUI/Stylesheets/ObfuscationSelectionDialog.qss"); - if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) { + + if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) + { QTextStream in(&stylesheetFile); QString stylesheet = in.readAll(); setStyleSheet(stylesheet); @@ -31,32 +34,40 @@ void ObfuscationSelectionDialog::setupStylesheet() { } } -void ObfuscationSelectionDialog::initUI() { +void ObfuscationSelectionDialog::initUI() +{ QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(30, 30, 30, 30); mainLayout->setSpacing(20); + titleLabel = new QLabel("Obfuscation Selection"); titleLabel->setObjectName("title"); titleLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(titleLabel); + descriptionLabel = new QLabel("Choose the Obfuscation Method you want to use for Syscall Generation:"); descriptionLabel->setObjectName("description"); descriptionLabel->setAlignment(Qt::AlignCenter); descriptionLabel->setWordWrap(true); mainLayout->addWidget(descriptionLabel); + QVBoxLayout* buttonLayout = new QVBoxLayout(); buttonLayout->setSpacing(15); + normalObfuscationButton = new QPushButton("Normal Obfuscation"); normalObfuscationButton->setToolTip("Runs w/ Obfuscation configured from the Obfuscation Settings for all Syscalls."); connect(normalObfuscationButton, &QPushButton::clicked, this, &ObfuscationSelectionDialog::onNormalObfuscationClicked); buttonLayout->addWidget(normalObfuscationButton); + stubMapperButton = new QPushButton("Stub Mapper"); stubMapperButton->setToolTip("Runs w/ Obfuscation configured from Stub Mapper for specially configured Syscalls."); connect(stubMapperButton, &QPushButton::clicked, this, &ObfuscationSelectionDialog::onStubMapperClicked); buttonLayout->addWidget(stubMapperButton); mainLayout->addLayout(buttonLayout); + QHBoxLayout* cancelLayout = new QHBoxLayout(); cancelLayout->addStretch(); + cancelButton = new QPushButton("Cancel"); cancelButton->setObjectName("cancel"); cancelButton->setFixedWidth(100); @@ -65,17 +76,20 @@ void ObfuscationSelectionDialog::initUI() { mainLayout->addLayout(cancelLayout); } -void ObfuscationSelectionDialog::onNormalObfuscationClicked() { +void ObfuscationSelectionDialog::onNormalObfuscationClicked() +{ selection = NormalObfuscation; accept(); } -void ObfuscationSelectionDialog::onStubMapperClicked() { +void ObfuscationSelectionDialog::onStubMapperClicked() +{ selection = StubMapper; accept(); } -void ObfuscationSelectionDialog::onCancelClicked() { +void ObfuscationSelectionDialog::onCancelClicked() +{ selection = Cancelled; reject(); -} +} diff --git a/Bind/src/GUI/Dialogs/SettingsDialog.cpp b/Bind/src/GUI/Dialogs/SettingsDialog.cpp index 340bf97..d44ff08 100644 --- a/Bind/src/GUI/Dialogs/SettingsDialog.cpp +++ b/Bind/src/GUI/Dialogs/SettingsDialog.cpp @@ -17,100 +17,137 @@ #include #include -SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) { +SettingsDialog::SettingsDialog(QWidget* parent) + : QDialog(parent) +{ setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); setMinimumSize(500, 600); + titleBar = new SettingsTitleBar(this); QString iniPath = PathUtils::getIniPath(); settings = new QSettings(iniPath, QSettings::IniFormat, this); + setupStylesheet(); initUI(); } -void SettingsDialog::initUI() { +void SettingsDialog::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(titleBar); + tabs = new QTabWidget(); + QScrollArea* generalScrollArea = new QScrollArea(); generalScrollArea->setWidgetResizable(true); generalScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); generalScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + generalTab = new GeneralTab(settings); generalTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); generalScrollArea->setWidget(generalTab); + QScrollArea* integrityScrollArea = new QScrollArea(); integrityScrollArea->setWidgetResizable(true); integrityScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); integrityScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + integrityTab = new IntegrityTab(settings); integrityTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); integrityScrollArea->setWidget(integrityTab); + QScrollArea* profileScrollArea = new QScrollArea(); profileScrollArea->setWidgetResizable(true); profileScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); profileScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + profileTab = new ProfileTab(settings); profileTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); profileScrollArea->setWidget(profileTab); + tabs->addTab(generalScrollArea, "General"); tabs->addTab(integrityScrollArea, "Integrity"); + bool inlineAssemblyEnabled = settings->value("general/inline_assembly", false).toBool(); bool indirectAssemblyEnabled = settings->value("general/indirect_assembly", false).toBool(); - if (inlineAssemblyEnabled) { + + if (inlineAssemblyEnabled) + { QScrollArea* inlineObfuscationScrollArea = new QScrollArea(); inlineObfuscationScrollArea->setWidgetResizable(true); inlineObfuscationScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); inlineObfuscationScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + inlineObfuscationTab = new InlineObfuscationTab(settings); inlineObfuscationTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); inlineObfuscationScrollArea->setWidget(inlineObfuscationTab); + tabs->addTab(inlineObfuscationScrollArea, "Obfuscation"); - } else if (indirectAssemblyEnabled) { + } + else if (indirectAssemblyEnabled) + { QScrollArea* indirectObfuscationScrollArea = new QScrollArea(); indirectObfuscationScrollArea->setWidgetResizable(true); indirectObfuscationScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); indirectObfuscationScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + indirectObfuscationTab = new IndirectObfuscationTab(settings); indirectObfuscationTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); indirectObfuscationScrollArea->setWidget(indirectObfuscationTab); + tabs->addTab(indirectObfuscationScrollArea, "Obfuscation"); - } else { + } + else + { QScrollArea* obfuscationScrollArea = new QScrollArea(); obfuscationScrollArea->setWidgetResizable(true); obfuscationScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); obfuscationScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + obfuscationTab = new ObfuscationTab(settings); obfuscationTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); obfuscationScrollArea->setWidget(obfuscationTab); + tabs->addTab(obfuscationScrollArea, "Obfuscation"); } + tabs->addTab(profileScrollArea, "Profile"); layout->addWidget(tabs); + QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->setSpacing(10); buttonLayout->setContentsMargins(20, 10, 20, 10); - if (!inlineAssemblyEnabled && !indirectAssemblyEnabled) { + + if (!inlineAssemblyEnabled && !indirectAssemblyEnabled) + { QPushButton* stubMapperBtn = new QPushButton("Stub Mapper"); stubMapperBtn->setToolTip("Customize Obfuscation Settings for Individual Syscalls"); connect(stubMapperBtn, &QPushButton::clicked, this, &SettingsDialog::openStubMapper); buttonLayout->addWidget(stubMapperBtn); } + QPushButton* saveBtn = new QPushButton("Save"); connect(saveBtn, &QPushButton::clicked, this, &SettingsDialog::saveSettings); + QPushButton* cancelBtn = new QPushButton("Cancel"); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); + buttonLayout->addStretch(); buttonLayout->addWidget(saveBtn); buttonLayout->addWidget(cancelBtn); + layout->addLayout(buttonLayout); connect(titleBar, &SettingsTitleBar::closeClicked, this, &SettingsDialog::reject); } -void SettingsDialog::setupStylesheet() { +void SettingsDialog::setupStylesheet() +{ QFile stylesheetFile(":/src/GUI/Stylesheets/SettingsDialog.qss"); - if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) { + + if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) + { QTextStream in(&stylesheetFile); QString stylesheet = in.readAll(); setStyleSheet(stylesheet); @@ -118,50 +155,69 @@ void SettingsDialog::setupStylesheet() { } } -void SettingsDialog::saveSettings() { +void SettingsDialog::saveSettings() +{ generalTab->saveSettings(); + bool inlineAssemblyEnabled = settings->value("general/inline_assembly", false).toBool(); bool indirectAssemblyEnabled = settings->value("general/indirect_assembly", false).toBool(); - if (inlineAssemblyEnabled) { - if (inlineObfuscationTab) { + + if (inlineAssemblyEnabled) + { + if (inlineObfuscationTab) + { inlineObfuscationTab->saveSettings(); } - } else if (indirectAssemblyEnabled) { - if (indirectObfuscationTab) { + } + else if (indirectAssemblyEnabled) + { + if (indirectObfuscationTab) + { indirectObfuscationTab->saveSettings(); } - } else { - if (obfuscationTab) { + } + else + { + if (obfuscationTab) + { obfuscationTab->saveSettings(); } } + integrityTab->saveSettings(); profileTab->saveSettings(); accept(); } -void SettingsDialog::openStubMapper() { +void SettingsDialog::openStubMapper() +{ StubMapperDialog dialog(this); dialog.exec(); } -void SettingsDialog::mousePressEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void SettingsDialog::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = true; m_dragPosition = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } -void SettingsDialog::mouseMoveEvent(QMouseEvent* event) { - if (event->buttons() & Qt::LeftButton && m_dragging) { +void SettingsDialog::mouseMoveEvent(QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton && m_dragging) + { move(event->globalPos() - m_dragPosition); event->accept(); } } -void SettingsDialog::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void SettingsDialog::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = false; event->accept(); } diff --git a/Bind/src/GUI/Dialogs/StubMapperDialog.cpp b/Bind/src/GUI/Dialogs/StubMapperDialog.cpp index 321db9d..a351dca 100644 --- a/Bind/src/GUI/Dialogs/StubMapperDialog.cpp +++ b/Bind/src/GUI/Dialogs/StubMapperDialog.cpp @@ -1,6 +1,7 @@ #include "include/GUI/Dialogs/StubMapperDialog.h" #include "include/Core/Utils/PathUtils.h" #include "include/GUI/Bars/SettingsTitleBar.h" +#include "include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h" #include #include #include @@ -21,129 +22,173 @@ StubMapperDialog::StubMapperDialog(QWidget* parent) initUI(); } -StubMapperDialog::~StubMapperDialog() { +StubMapperDialog::~StubMapperDialog() +{ delete settings; } -void StubMapperDialog::initUI() { +void StubMapperDialog::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(titleBar); + QSplitter* splitter = new QSplitter(Qt::Horizontal); + QWidget* leftPanel = new QWidget(); QVBoxLayout* leftLayout = new QVBoxLayout(leftPanel); + QGroupBox* syscallGroup = new QGroupBox("Available Syscalls"); QVBoxLayout* syscallLayout = new QVBoxLayout(); QHBoxLayout* filterLayout = new QHBoxLayout(); + QLabel* filterLabel = new QLabel("Filter:"); filterInput = new QLineEdit(); filterInput->setPlaceholderText("Filter Syscalls..."); + connect(filterInput, &QLineEdit::textChanged, this, &StubMapperDialog::filterSyscalls); filterLayout->addWidget(filterLabel); filterLayout->addWidget(filterInput); syscallLayout->addLayout(filterLayout); + syscallList = new QListWidget(); connect(syscallList, &QListWidget::currentItemChanged, this, &StubMapperDialog::onSyscallSelected); loadSyscalls(); syscallLayout->addWidget(syscallList); syscallGroup->setLayout(syscallLayout); leftLayout->addWidget(syscallGroup); + QWidget* rightPanel = new QWidget(); QVBoxLayout* rightLayout = new QVBoxLayout(rightPanel); + QGroupBox* settingsGroup = new QGroupBox("Stub Configuration"); QVBoxLayout* settingsLayout = new QVBoxLayout(); + currentSyscallLabel = new QLabel("Select a Syscall from the list"); QFont boldFont = currentSyscallLabel->font(); boldFont.setBold(true); currentSyscallLabel->setFont(boldFont); settingsLayout->addWidget(currentSyscallLabel); + settingsTabs = new QTabWidget(); + QWidget* junkTab = new QWidget(); QFormLayout* junkLayout = new QFormLayout(junkTab); + enableJunk = new QCheckBox("Enable Junk Instructions"); connect(enableJunk, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); junkLayout->addRow(enableJunk); + minInstructions = new QSpinBox(); minInstructions->setRange(1, 10); minInstructions->setValue(2); - connect(minInstructions, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(minInstructions, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); junkLayout->addRow("Minimum Instructions:", minInstructions); + maxInstructions = new QSpinBox(); maxInstructions->setRange(1, 20); maxInstructions->setValue(8); - connect(maxInstructions, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(maxInstructions, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); junkLayout->addRow("Maximum Instructions:", maxInstructions); + useAdvancedJunk = new QCheckBox("Advanced Junk Instructions"); connect(useAdvancedJunk, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); junkLayout->addRow(useAdvancedJunk); + QWidget* encryptionTab = new QWidget(); QFormLayout* encryptionLayout = new QFormLayout(encryptionTab); + enableEncryption = new QCheckBox("Enable Syscall ID Encryption"); connect(enableEncryption, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); encryptionLayout->addRow(enableEncryption); + encryptionMethod = new QComboBox(); encryptionMethod->addItem("Basic XOR (Simple)", 1); encryptionMethod->addItem("Multi-Key XOR (Medium)", 2); encryptionMethod->addItem("Add + XOR (Medium)", 3); encryptionMethod->addItem("Enhanced XOR (Medium)", 4); encryptionMethod->addItem("Offset Shifting (Medium)", 5); - connect(encryptionMethod, QOverload::of(&QComboBox::currentIndexChanged), this, &StubMapperDialog::onSettingChanged); + connect(encryptionMethod, QOverload::of(&QComboBox::currentIndexChanged), + this, &StubMapperDialog::onSettingChanged); encryptionLayout->addRow("Encryption Method:", encryptionMethod); + QWidget* structureTab = new QWidget(); QFormLayout* structureLayout = new QFormLayout(structureTab); + enableChunking = new QCheckBox("Enable Function Chunking"); connect(enableChunking, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); structureLayout->addRow(enableChunking); + enableInterleaved = new QCheckBox("Enable Interleaved Execution"); connect(enableInterleaved, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); structureLayout->addRow(enableInterleaved); + shuffleSequence = new QCheckBox("Enable Sequence Shuffling"); connect(shuffleSequence, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); structureLayout->addRow(shuffleSequence); + QWidget* nameTab = new QWidget(); QFormLayout* nameLayout = new QFormLayout(nameTab); QHBoxLayout* syscallNameLayout = new QHBoxLayout(); + syscallPrefixLength = new QSpinBox(); syscallPrefixLength->setRange(4, 16); syscallPrefixLength->setValue(8); - connect(syscallPrefixLength, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(syscallPrefixLength, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); + syscallNumberLength = new QSpinBox(); syscallNumberLength->setRange(4, 16); syscallNumberLength->setValue(6); - connect(syscallNumberLength, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(syscallNumberLength, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); + syscallNameLayout->addWidget(new QLabel("Chars:")); syscallNameLayout->addWidget(syscallPrefixLength); syscallNameLayout->addWidget(new QLabel("Numbers:")); syscallNameLayout->addWidget(syscallNumberLength); nameLayout->addRow("Syscall Name Length:", syscallNameLayout); + offsetNameLength = new QSpinBox(); offsetNameLength->setRange(4, 16); offsetNameLength->setValue(8); - connect(offsetNameLength, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(offsetNameLength, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); nameLayout->addRow("Offset Name Length:", offsetNameLength); + QWidget* controlFlowTab = new QWidget(); QFormLayout* controlFlowLayout = new QFormLayout(controlFlowTab); + enableControlFlow = new QCheckBox("Enable Control Flow"); connect(enableControlFlow, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); controlFlowLayout->addRow(enableControlFlow); + opaquePredicates = new QCheckBox("Opaque Predicates"); connect(opaquePredicates, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); controlFlowLayout->addRow(opaquePredicates); + bogusControlFlow = new QCheckBox("Bogus Control Flow"); connect(bogusControlFlow, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow(bogusControlFlow); + controlFlowLayout->addRow(bogusControlFlow); + indirectJumps = new QCheckBox("Indirect Jumps"); connect(indirectJumps, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow(indirectJumps); + controlFlowLayout->addRow(indirectJumps); + conditionalBranches = new QCheckBox("Conditional Branches"); connect(conditionalBranches, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow(conditionalBranches); + controlFlowLayout->addRow(conditionalBranches); + controlFlowComplexity = new QSpinBox(); controlFlowComplexity->setRange(1, 10); controlFlowComplexity->setValue(2); - connect(controlFlowComplexity, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + connect(controlFlowComplexity, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); + controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + settingsTabs->addTab(junkTab, "Junk Instructions"); settingsTabs->addTab(encryptionTab, "Encryption"); settingsTabs->addTab(structureTab, "Structure"); @@ -152,23 +197,31 @@ void StubMapperDialog::initUI() { settingsLayout->addWidget(settingsTabs); settingsGroup->setLayout(settingsLayout); rightLayout->addWidget(settingsGroup); + splitter->addWidget(leftPanel); splitter->addWidget(rightPanel); splitter->setSizes({300, 500}); layout->addWidget(splitter); + QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->setSpacing(10); buttonLayout->setContentsMargins(20, 10, 20, 10); + useGlobalBtn = new QPushButton("Use Global Settings"); connect(useGlobalBtn, &QPushButton::clicked, this, &StubMapperDialog::useGlobalSettings); + resetBtn = new QPushButton("Reset"); connect(resetBtn, &QPushButton::clicked, this, &StubMapperDialog::resetCurrentSettings); + QPushButton* validateBtn = new QPushButton("Validate"); connect(validateBtn, &QPushButton::clicked, this, &StubMapperDialog::validateCurrentSettings); + QPushButton* saveBtn = new QPushButton("Save"); connect(saveBtn, &QPushButton::clicked, this, &StubMapperDialog::saveSettings); + QPushButton* cancelBtn = new QPushButton("Cancel"); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); + buttonLayout->addWidget(useGlobalBtn); buttonLayout->addWidget(resetBtn); buttonLayout->addWidget(validateBtn); @@ -176,14 +229,18 @@ void StubMapperDialog::initUI() { buttonLayout->addWidget(saveBtn); buttonLayout->addWidget(cancelBtn); layout->addLayout(buttonLayout); + connect(titleBar, &SettingsTitleBar::closeClicked, this, &QDialog::reject); setupStylesheet(); enableControls(false); } -void StubMapperDialog::setupStylesheet() { +void StubMapperDialog::setupStylesheet() +{ QFile stylesheetFile(":/src/GUI/Stylesheets/StubMapperDialog.qss"); - if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) { + + if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) + { QTextStream in(&stylesheetFile); QString stylesheet = in.readAll(); setStyleSheet(stylesheet); @@ -191,101 +248,143 @@ void StubMapperDialog::setupStylesheet() { } } -void StubMapperDialog::mousePressEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void StubMapperDialog::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = true; m_dragPosition = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } -void StubMapperDialog::mouseMoveEvent(QMouseEvent* event) { - if (event->buttons() & Qt::LeftButton && m_dragging) { +void StubMapperDialog::mouseMoveEvent(QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton && m_dragging) + { move(event->globalPos() - m_dragPosition); event->accept(); } } -void StubMapperDialog::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void StubMapperDialog::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = false; event->accept(); } } -void StubMapperDialog::loadSyscalls() { +void StubMapperDialog::loadSyscalls() +{ syscallList->clear(); QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList(); - if (selectedSyscalls.isEmpty()) { + + if (selectedSyscalls.isEmpty()) + { QString headerPath = PathUtils::getSysCallerPath() + "/Wrapper/include/Sys/sysFunctions.h"; QString syscallMode = settings->value("general/syscall_mode", "Nt").toString(); QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK"; + QFile headerFile(headerPath); - if (headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + + if (headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { QTextStream in(&headerFile); - while (!in.atEnd()) { + + while (!in.atEnd()) + { QString line = in.readLine(); QRegularExpression regex(QString(R"(extern "C" (?:NTSTATUS|ULONG) (%1\w+)\()").arg(syscallPrefix)); QRegularExpressionMatch match = regex.match(line); - if (match.hasMatch()) { + + if (match.hasMatch()) + { selectedSyscalls.append(match.captured(1)); } + QRegularExpression scRegex(R"(extern "C" (?:NTSTATUS|ULONG) (SC\w+)\()"); QRegularExpressionMatch scMatch = scRegex.match(line); - if (scMatch.hasMatch()) { + + if (scMatch.hasMatch()) + { QString syscallName = syscallPrefix + scMatch.captured(1).mid(2); selectedSyscalls.append(syscallName); } } + headerFile.close(); } + selectedSyscalls.sort(); } - for (const QString& syscall : selectedSyscalls) { + + for (const QString& syscall : selectedSyscalls) + { QListWidgetItem* item = new QListWidgetItem(syscall); - if (syscallSettings.contains(syscall)) { + + if (syscallSettings.contains(syscall)) + { item->setForeground(Qt::green); } + syscallList->addItem(item); } } -void StubMapperDialog::filterSyscalls(const QString& text) { - for (int i = 0; i < syscallList->count(); ++i) { +void StubMapperDialog::filterSyscalls(const QString& text) +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (item->text().toLower().contains(text.toLower())) { + + if (item->text().toLower().contains(text.toLower())) + { item->setHidden(false); - } else { + } + else + { item->setHidden(true); } } } -void StubMapperDialog::onSyscallSelected(QListWidgetItem* current, QListWidgetItem* previous) { - if (current == nullptr) { +void StubMapperDialog::onSyscallSelected(QListWidgetItem* current, QListWidgetItem* previous) +{ + if (current == nullptr) + { enableControls(false); currentSyscallLabel->setText("Select a Syscall from the list"); return; } + enableControls(true); QString syscallName = current->text(); currentSyscallLabel->setText(QString("Configuring: %1").arg(syscallName)); loadSyscallSpecificSettings(syscallName); } -void StubMapperDialog::loadSyscallSpecificSettings(const QString& syscallName) { - if (syscallSettings.contains(syscallName)) { +void StubMapperDialog::loadSyscallSpecificSettings(const QString& syscallName) +{ + if (syscallSettings.contains(syscallName)) + { QMap settings = syscallSettings[syscallName].toMap(); + enableJunk->setChecked(settings.value("enable_junk", false).toBool()); minInstructions->setValue(settings.value("min_instructions", 2).toInt()); maxInstructions->setValue(settings.value("max_instructions", 8).toInt()); useAdvancedJunk->setChecked(settings.value("use_advanced_junk", false).toBool()); enableEncryption->setChecked(settings.value("enable_encryption", false).toBool()); - int encryptionMethodValue = settings.value("encryption_method", 1).toInt(); + + int encryptionMethodValue = settings.value("encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt(); int index = encryptionMethod->findData(encryptionMethodValue); - if (index >= 0) { + + if (index >= 0) + { encryptionMethod->setCurrentIndex(index); } + enableChunking->setChecked(settings.value("enable_chunking", false).toBool()); enableInterleaved->setChecked(settings.value("enable_interleaved", false).toBool()); shuffleSequence->setChecked(settings.value("shuffle_sequence", false).toBool()); @@ -298,22 +397,29 @@ void StubMapperDialog::loadSyscallSpecificSettings(const QString& syscallName) { indirectJumps->setChecked(settings.value("control_flow_indirect_jumps", false).toBool()); conditionalBranches->setChecked(settings.value("control_flow_conditional_branches", false).toBool()); controlFlowComplexity->setValue(settings.value("control_flow_complexity", 2).toInt()); - } else { + } + else + { loadGlobalSettings(); } } -void StubMapperDialog::loadGlobalSettings() { +void StubMapperDialog::loadGlobalSettings() +{ enableJunk->setChecked(true); minInstructions->setValue(settings->value("obfuscation/min_instructions", 2).toInt()); maxInstructions->setValue(settings->value("obfuscation/max_instructions", 8).toInt()); useAdvancedJunk->setChecked(settings->value("obfuscation/use_advanced_junk", false).toBool()); enableEncryption->setChecked(settings->value("obfuscation/enable_encryption", true).toBool()); - int encryptionMethodValue = settings->value("obfuscation/encryption_method", 1).toInt(); + + int encryptionMethodValue = settings->value("obfuscation/encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt(); int index = encryptionMethod->findData(encryptionMethodValue); - if (index >= 0) { + + if (index >= 0) + { encryptionMethod->setCurrentIndex(index); } + enableChunking->setChecked(settings->value("obfuscation/enable_chunking", true).toBool()); enableInterleaved->setChecked(settings->value("obfuscation/enable_interleaved", true).toBool()); shuffleSequence->setChecked(settings->value("obfuscation/shuffle_sequence", true).toBool()); @@ -328,26 +434,38 @@ void StubMapperDialog::loadGlobalSettings() { controlFlowComplexity->setValue(settings->value("obfuscation/control_flow_complexity", 2).toInt()); } -void StubMapperDialog::useGlobalSettings() { +void StubMapperDialog::useGlobalSettings() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); - if (syscallSettings.contains(syscallName)) { + + if (syscallSettings.contains(syscallName)) + { syscallSettings.remove(syscallName); currentItem->setForeground(Qt::white); } + loadGlobalSettings(); } } -void StubMapperDialog::resetCurrentSettings() { +void StubMapperDialog::resetCurrentSettings() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); - if (syscallSettings.contains(syscallName)) { + + if (syscallSettings.contains(syscallName)) + { syscallSettings.remove(syscallName); currentItem->setForeground(Qt::white); } + enableJunk->setChecked(true); minInstructions->setValue(2); maxInstructions->setValue(8); @@ -369,17 +487,22 @@ void StubMapperDialog::resetCurrentSettings() { } } -void StubMapperDialog::onSettingChanged() { +void StubMapperDialog::onSettingChanged() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); saveCurrentSyscallSettings(syscallName); currentItem->setForeground(Qt::green); } } -void StubMapperDialog::saveCurrentSyscallSettings(const QString& syscallName) { +void StubMapperDialog::saveCurrentSyscallSettings(const QString& syscallName) +{ QMap settings; + settings["enable_junk"] = enableJunk->isChecked(); settings["min_instructions"] = minInstructions->value(); settings["max_instructions"] = maxInstructions->value(); @@ -398,110 +521,157 @@ void StubMapperDialog::saveCurrentSyscallSettings(const QString& syscallName) { settings["control_flow_indirect_jumps"] = indirectJumps->isChecked(); settings["control_flow_conditional_branches"] = conditionalBranches->isChecked(); settings["control_flow_complexity"] = controlFlowComplexity->value(); + syscallSettings[syscallName] = QVariant::fromValue(settings); } -void StubMapperDialog::validateCurrentSettings() { +void StubMapperDialog::validateCurrentSettings() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); - if (syscallSettings.contains(syscallName)) { + + if (syscallSettings.contains(syscallName)) + { QMap settings = syscallSettings[syscallName].toMap(); QString errorMessage; bool isValid = validateStubSettings(settings, errorMessage); - if (isValid) { + + if (isValid) + { showValidationSuccess(QString("Settings for %1 are Valid!").arg(syscallName)); - } else { + } + else + { showValidationError(QString("Settings for %1 are Invalid: %2").arg(syscallName, errorMessage)); } - } else { + } + else + { showValidationSuccess(QString("Using Global Settings for %1.").arg(syscallName)); } - } else { - QMessageBox::warning(this, "Bind - v1.3.0", "Please select a Syscall first."); + } + else + { + QMessageBox::warning(this, "Bind - v1.3.1", "Please select a Syscall first."); } } -void StubMapperDialog::enableControls(bool enabled) { +void StubMapperDialog::enableControls(bool enabled) +{ settingsTabs->setEnabled(enabled); useGlobalBtn->setEnabled(enabled); resetBtn->setEnabled(enabled); } -void StubMapperDialog::loadSyscallSettings() { +void StubMapperDialog::loadSyscallSettings() +{ syscallSettings = settings->value("stub_mapper/syscall_settings", QMap()).toMap(); } -void StubMapperDialog::saveSettings() { +void StubMapperDialog::saveSettings() +{ QStringList invalidSyscalls; - for (auto it = syscallSettings.begin(); it != syscallSettings.end(); ++it) { + + for (auto it = syscallSettings.begin(); it != syscallSettings.end(); ++it) + { QString syscallName = it.key(); QMap settings = it.value().toMap(); QString errorMessage; bool isValid = validateStubSettings(settings, errorMessage); - if (!isValid) { + + if (!isValid) + { invalidSyscalls.append(QString("%1: %2").arg(syscallName, errorMessage)); } } - if (!invalidSyscalls.isEmpty()) { + + if (!invalidSyscalls.isEmpty()) + { QString errorMessage = "The following Syscalls have Invalid Settings:\n\n" + invalidSyscalls.join("\n"); showValidationError(errorMessage); return; } + settings->setValue("stub_mapper/syscall_settings", QVariant::fromValue(syscallSettings)); - QMessageBox::information(this, "Bind - v1.3.0", "Custom Syscall Settings have been saved successfully."); + QMessageBox::information(this, "Bind - v1.3.1", "Custom Syscall Settings have been saved successfully."); accept(); } -bool StubMapperDialog::validateStubSettings(const QMap& settings, QString& errorMessage) { - QStringList requiredKeys = { +bool StubMapperDialog::validateStubSettings(const QMap& settings, QString& errorMessage) +{ + QStringList requiredKeys = + { "enable_junk", "min_instructions", "max_instructions", "use_advanced_junk", "enable_encryption", "encryption_method", "enable_chunking", "enable_interleaved", "shuffle_sequence", "syscall_prefix_length", "syscall_number_length", "offset_name_length" }; - for (const QString& key : requiredKeys) { - if (!settings.contains(key)) { + + for (const QString& key : requiredKeys) + { + if (!settings.contains(key)) + { errorMessage = QString("Missing Required Setting: %1").arg(key); return false; } } - if (settings["min_instructions"].toInt() < 1 || settings["min_instructions"].toInt() > 10) { + + if (settings["min_instructions"].toInt() < 1 || settings["min_instructions"].toInt() > 10) + { errorMessage = "Minimum Instructions Must Be Between 1 And 10"; return false; } - if (settings["max_instructions"].toInt() < 1 || settings["max_instructions"].toInt() > 20) { + + if (settings["max_instructions"].toInt() < 1 || settings["max_instructions"].toInt() > 20) + { errorMessage = "Maximum Instructions Must Be Between 1 And 20"; return false; } - if (settings["min_instructions"].toInt() > settings["max_instructions"].toInt()) { + + if (settings["min_instructions"].toInt() > settings["max_instructions"].toInt()) + { errorMessage = "Minimum Instructions Cannot Be Greater Than Maximum Instructions"; return false; } - if (settings["syscall_prefix_length"].toInt() < 4 || settings["syscall_prefix_length"].toInt() > 16) { + + if (settings["syscall_prefix_length"].toInt() < 4 || settings["syscall_prefix_length"].toInt() > 16) + { errorMessage = "Syscall Prefix Length Must Be Between 4 And 16"; return false; } - if (settings["syscall_number_length"].toInt() < 4 || settings["syscall_number_length"].toInt() > 16) { + + if (settings["syscall_number_length"].toInt() < 4 || settings["syscall_number_length"].toInt() > 16) + { errorMessage = "Syscall Number Length Must Be Between 4 And 16"; return false; } - if (settings["offset_name_length"].toInt() < 4 || settings["offset_name_length"].toInt() > 16) { + + if (settings["offset_name_length"].toInt() < 4 || settings["offset_name_length"].toInt() > 16) + { errorMessage = "Offset Name Length Must Be Between 4 And 16"; return false; } + QList validEncryptionMethods = {1, 2, 3, 4, 5}; - if (!validEncryptionMethods.contains(settings["encryption_method"].toInt())) { + + if (!validEncryptionMethods.contains(settings["encryption_method"].toInt())) + { errorMessage = QString("Invalid Encryption Method: %1").arg(settings["encryption_method"].toInt()); return false; } + return true; } -void StubMapperDialog::showValidationError(const QString& message) { - QMessageBox::critical(this, "Bind - v1.3.0", message); +void StubMapperDialog::showValidationError(const QString& message) +{ + QMessageBox::critical(this, "Bind - v1.3.1", message); } -void StubMapperDialog::showValidationSuccess(const QString& message) { - QMessageBox::information(this, "Bind - v1.3.0", message); +void StubMapperDialog::showValidationSuccess(const QString& message) +{ + QMessageBox::information(this, "Bind - v1.3.1", message); } diff --git a/Bind/src/GUI/MainWindow.cpp b/Bind/src/GUI/MainWindow.cpp index cf5ef12..acf84d0 100644 --- a/Bind/src/GUI/MainWindow.cpp +++ b/Bind/src/GUI/MainWindow.cpp @@ -22,12 +22,19 @@ #include #include -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), dragPos(0, 0), validatorThread(nullptr), compatibilityThread(nullptr), verificationThread(nullptr), obfuscationThread(nullptr) { - setWindowTitle("Bind - v1.3.0"); +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , dragPos(0, 0) + , validatorThread(nullptr) + , compatibilityThread(nullptr) + , verificationThread(nullptr) + , obfuscationThread(nullptr) +{ + setWindowTitle("Bind - v1.3.1"); setMinimumSize(1400, 900); setWindowFlags(Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground); - + auto* central = new QWidget(); central->setStyleSheet( "QWidget {" @@ -35,292 +42,414 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), dragPos(0, 0), va " border-radius: 15px;" "}" ); + setCentralWidget(central); + auto* mainLayout = new QVBoxLayout(central); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); + titleBar = new TitleBar(this); mainLayout->addWidget(titleBar); + auto* contentLayout = new QHBoxLayout(); contentLayout->setContentsMargins(20, 20, 20, 20); contentLayout->setSpacing(20); mainLayout->addLayout(contentLayout); + leftPanel = new LeftPanel(this); contentLayout->addWidget(leftPanel); + rightPanel = new RightPanel(this); contentLayout->addWidget(rightPanel, 2); + statusBar = new StatusBar(); mainLayout->addWidget(statusBar); + connect(leftPanel, &LeftPanel::settingsButtonClicked, this, &MainWindow::showSettings); connect(leftPanel, &LeftPanel::validationButtonClicked, this, &MainWindow::runValidation); connect(leftPanel, &LeftPanel::compatibilityButtonClicked, this, &MainWindow::runCompatibility); connect(leftPanel, &LeftPanel::verificationButtonClicked, this, &MainWindow::runVerification); connect(leftPanel, &LeftPanel::obfuscationButtonClicked, this, &MainWindow::runObfuscation); + PathUtils::debugPathDetection(); + connect(titleBar, &TitleBar::minimizeClicked, this, &MainWindow::minimizeWindow); connect(titleBar, &TitleBar::maximizeClicked, this, &MainWindow::toggleMaximize); connect(titleBar, &TitleBar::closeClicked, this, &MainWindow::closeWindow); } -MainWindow::~MainWindow() { - if (validatorThread) { +MainWindow::~MainWindow() +{ + if (validatorThread) + { validatorThread->quit(); validatorThread->wait(); delete validatorThread; } - if (compatibilityThread) { + + if (compatibilityThread) + { compatibilityThread->quit(); compatibilityThread->wait(); delete compatibilityThread; } - if (verificationThread) { + + if (verificationThread) + { verificationThread->quit(); verificationThread->wait(); delete verificationThread; } - if (obfuscationThread) { + + if (obfuscationThread) + { obfuscationThread->quit(); obfuscationThread->wait(); delete obfuscationThread; } } -void MainWindow::mousePressEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void MainWindow::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { dragPos = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } -void MainWindow::mouseMoveEvent(QMouseEvent* event) { - if (event->buttons() == Qt::LeftButton) { +void MainWindow::mouseMoveEvent(QMouseEvent* event) +{ + if (event->buttons() == Qt::LeftButton) + { move(event->globalPos() - dragPos); event->accept(); } } -void MainWindow::showSettings() { +void MainWindow::showSettings() +{ SettingsDialog dialog(this); dialog.exec(); } -void MainWindow::saveAllSettings() { - try { +void MainWindow::saveAllSettings() +{ + try + { QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); - } catch (...) { + } + catch (...) + { } } -void MainWindow::runValidation() { - if (validatorThread && validatorThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Validation Check is already running. Please wait for it to complete."); +void MainWindow::runValidation() +{ + if (validatorThread && validatorThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Validation Check is already running. Please wait for it to complete."); return; } + QStringList dllPaths = leftPanel->getDllPaths(); - if (dllPaths.isEmpty()) { - QMessageBox::warning(this, "Bind - v1.3.0", "No DLL Paths specified. Please add at least one NTDLL path."); + + if (dllPaths.isEmpty()) + { + QMessageBox::warning(this, "Bind - v1.3.1", "No DLL Paths specified. Please add at least one NTDLL path."); return; } - if (validatorThread) { + + if (validatorThread) + { validatorThread->deleteLater(); } + validatorThread = new ValidatorThread(this); validatorThread->setDllPaths(dllPaths); - connect(validatorThread, &ValidatorThread::validationStarted, this, [this]() { + + connect(validatorThread, &ValidatorThread::validationStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Validation Started..."); statusBar->updateStatus("Running Validation Check..."); rightPanel->getOutputPanel()->clearText(); }); - connect(validatorThread, &ValidatorThread::progressUpdated, this, [this](const QString& status) { + + connect(validatorThread, &ValidatorThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(validatorThread, &ValidatorThread::validationFinished, this, [this](bool success, const QString& message) { + + connect(validatorThread, &ValidatorThread::validationFinished, this, [this](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - - if (success) { + + if (success) + { leftPanel->updateStatus("Validation Completed!"); statusBar->updateStatus("Validation Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Validation Failed!"); statusBar->updateStatus("Validation Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + validatorThread->deleteLater(); validatorThread = nullptr; }); + validatorThread->start(); } -void MainWindow::minimizeWindow() { +void MainWindow::minimizeWindow() +{ showMinimized(); } -void MainWindow::toggleMaximize() { - if (isMaximized()) { +void MainWindow::toggleMaximize() +{ + if (isMaximized()) + { showNormal(); - } else { + } + else + { showMaximized(); } } -void MainWindow::closeWindow() { +void MainWindow::closeWindow() +{ close(); QApplication::quit(); } -void MainWindow::runCompatibility() { - if (compatibilityThread && compatibilityThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Compatibility Check is already running. Please wait for it to complete."); +void MainWindow::runCompatibility() +{ + if (compatibilityThread && compatibilityThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Compatibility Check is already running. Please wait for it to complete."); return; } + QStringList dllPaths = leftPanel->getDllPaths(); - if (dllPaths.isEmpty()) { - QMessageBox::warning(this, "Bind - v1.3.0", "No DLL Paths specified. Please add at least one NTDLL path."); + + if (dllPaths.isEmpty()) + { + QMessageBox::warning(this, "Bind - v1.3.1", "No DLL Paths specified. Please add at least one NTDLL path."); return; } - if (compatibilityThread) { + + if (compatibilityThread) + { compatibilityThread->deleteLater(); } + compatibilityThread = new CompatibilityThread(this); compatibilityThread->setDllPaths(dllPaths); - connect(compatibilityThread, &CompatibilityThread::compatibilityStarted, this, [this]() { + + connect(compatibilityThread, &CompatibilityThread::compatibilityStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Compatibility Started..."); statusBar->updateStatus("Running Compatibility Check..."); rightPanel->getOutputPanel()->clearText(); }); - connect(compatibilityThread, &CompatibilityThread::progressUpdated, this, [this](const QString& status) { + + connect(compatibilityThread, &CompatibilityThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(compatibilityThread, &CompatibilityThread::compatibilityFinished, this, [this](bool success, const QString& message) { + + connect(compatibilityThread, &CompatibilityThread::compatibilityFinished, this, [this](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - - if (success) { + + if (success) + { leftPanel->updateStatus("Compatibility Completed!"); statusBar->updateStatus("Compatibility Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Compatibility Failed!"); statusBar->updateStatus("Compatibility Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + compatibilityThread->deleteLater(); compatibilityThread = nullptr; }); + compatibilityThread->start(); } -void MainWindow::runVerification() { - if (verificationThread && verificationThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Verification Check is already running. Please wait for it to complete."); +void MainWindow::runVerification() +{ + if (verificationThread && verificationThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Verification Check is already running. Please wait for it to complete."); return; } + QStringList dllPaths = leftPanel->getDllPaths(); - if (dllPaths.isEmpty()) { - QMessageBox::warning(this, "Bind - v1.3.0", "No DLL Paths specified. Please add at least one NTDLL path."); + + if (dllPaths.isEmpty()) + { + QMessageBox::warning(this, "Bind - v1.3.1", "No DLL Paths specified. Please add at least one NTDLL path."); return; } - if (verificationThread) { + + if (verificationThread) + { verificationThread->deleteLater(); } + verificationThread = new VerificationThread(this); verificationThread->setDllPaths(dllPaths); - verificationThread->setOutputCallback([this](const QString& message) { + + verificationThread->setOutputCallback([this](const QString& message) + { leftPanel->updateStatus(message); statusBar->updateStatus(message); rightPanel->getOutputPanel()->appendText(message); }); - connect(verificationThread, &VerificationThread::verificationStarted, this, [this]() { + + connect(verificationThread, &VerificationThread::verificationStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Verification Started..."); statusBar->updateStatus("Running Verification Check..."); rightPanel->getOutputPanel()->clearText(); }); - connect(verificationThread, &VerificationThread::progressUpdated, this, [this](const QString& status) { + + connect(verificationThread, &VerificationThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(verificationThread, &VerificationThread::verificationFinished, this, [this](bool success, const QString& message) { + + connect(verificationThread, &VerificationThread::verificationFinished, this, [this](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - if (success) { + + if (success) + { leftPanel->updateStatus("Verification Completed!"); statusBar->updateStatus("Verification Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Verification Failed!"); statusBar->updateStatus("Verification Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + verificationThread->deleteLater(); verificationThread = nullptr; }); + verificationThread->start(); } -void MainWindow::runObfuscation() { - if (obfuscationThread && obfuscationThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Syscall Obfuscation is already running. Please wait for it to complete."); +void MainWindow::runObfuscation() +{ + if (obfuscationThread && obfuscationThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Syscall Obfuscation is already running. Please wait for it to complete."); return; } + saveAllSettings(); + ObfuscationSelectionDialog dialog(this); - if (dialog.exec() != QDialog::Accepted) { + + if (dialog.exec() != QDialog::Accepted) + { return; } + QString selectedMethod; ObfuscationSelectionDialog::Selection selection = dialog.getSelection(); - if (selection == ObfuscationSelectionDialog::NormalObfuscation) { + + if (selection == ObfuscationSelectionDialog::NormalObfuscation) + { selectedMethod = "normal"; QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); settings.setValue("obfuscation/force_normal", true); settings.setValue("obfuscation/force_stub_mapper", false); settings.setValue("obfuscation/last_method", "normal"); - } else if (selection == ObfuscationSelectionDialog::StubMapper) { + } + else if (selection == ObfuscationSelectionDialog::StubMapper) + { selectedMethod = "stub_mapper"; QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); settings.setValue("obfuscation/force_normal", false); settings.setValue("obfuscation/force_stub_mapper", true); settings.setValue("obfuscation/last_method", "stub_mapper"); - } else { + } + else + { return; } - if (obfuscationThread) { + + if (obfuscationThread) + { obfuscationThread->deleteLater(); } + obfuscationThread = new ObfuscationThread(this); - connect(obfuscationThread, &ObfuscationThread::obfuscationStarted, this, [this]() { + + connect(obfuscationThread, &ObfuscationThread::obfuscationStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Obfuscation Started..."); statusBar->updateStatus("Running Syscall Obfuscation..."); rightPanel->getOutputPanel()->clearText(); }); - connect(obfuscationThread, &ObfuscationThread::progressUpdated, this, [this](const QString& status) { + + connect(obfuscationThread, &ObfuscationThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(obfuscationThread, &ObfuscationThread::obfuscationFinished, this, [this, selectedMethod](bool success, const QString& message) { + + connect(obfuscationThread, &ObfuscationThread::obfuscationFinished, this, [this, selectedMethod](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - if (success) { + + if (success) + { leftPanel->updateStatus("Obfuscation Completed!"); statusBar->updateStatus("Obfuscation Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Obfuscation Failed!"); statusBar->updateStatus("Obfuscation Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); settings.remove("obfuscation/force_normal"); settings.remove("obfuscation/force_stub_mapper"); + obfuscationThread->deleteLater(); obfuscationThread = nullptr; }); + obfuscationThread->start(); } -void MainWindow::closeEvent(QCloseEvent* event) { -} +void MainWindow::closeEvent(QCloseEvent* event) +{} diff --git a/Bind/src/GUI/Panels/LeftPanel.cpp b/Bind/src/GUI/Panels/LeftPanel.cpp index 6a761e1..f540d89 100644 --- a/Bind/src/GUI/Panels/LeftPanel.cpp +++ b/Bind/src/GUI/Panels/LeftPanel.cpp @@ -14,7 +14,9 @@ #include #include -LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { +LeftPanel::LeftPanel(QWidget* parent) + : QFrame(parent) +{ setMaximumWidth(350); setStyleSheet( "QFrame {" @@ -25,16 +27,20 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { auto* layout = new QVBoxLayout(this); layout->setContentsMargins(20, 20, 20, 20); layout->setSpacing(10); + auto* topSection = new QVBoxLayout(); topSection->setSpacing(5); topSection->setAlignment(Qt::AlignCenter); + logoImage = new QLabel(this); - logoImage->setPixmap(QPixmap(":/src/Res/Icons/syscaller.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + logoImage->setPixmap(QPixmap(":/src/Res/Icons/syscaller.png") + .scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); logoImage->setFixedSize(128, 128); logoImage->setAlignment(Qt::AlignCenter); logoImage->setStyleSheet("QLabel { background: transparent; }"); logoImage->setContentsMargins(0, 0, 0, 0); topSection->addWidget(logoImage, 0, Qt::AlignCenter); + logoLabel = new QLabel("SysCaller: Bind", this); logoLabel->setStyleSheet( "color: #0077d4;" @@ -45,13 +51,15 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { ); logoLabel->setAlignment(Qt::AlignCenter); topSection->addWidget(logoLabel, 0, Qt::AlignCenter); - versionLabel = new QLabel("v1.3.0", this); + + versionLabel = new QLabel("v1.3.1", this); versionLabel->setStyleSheet("color: #666666; font-size: 12px;"); versionLabel->setAlignment(Qt::AlignCenter); versionLabel->setCursor(Qt::PointingHandCursor); versionLabel->setTextFormat(Qt::RichText); - versionLabel->setText("v1.3.0"); + versionLabel->setText("v1.3.1"); topSection->addWidget(versionLabel, 0, Qt::AlignCenter); + layout->addLayout(topSection); layout->addSpacing(15); auto* dllFrame = new QFrame(this); @@ -62,16 +70,20 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { " padding: 10px;" "}" ); + auto* dllLayout = new QVBoxLayout(dllFrame); dllLayout->setContentsMargins(15, 15, 15, 15); dllLayout->setSpacing(8); + auto* headerBtnLayout = new QHBoxLayout(); auto* dllHeader = new QLabel("NTDLL PATHS", this); dllHeader->setStyleSheet("color: #888888; font-size: 12px; font-weight: bold;"); headerBtnLayout->addWidget(dllHeader); headerBtnLayout->addStretch(); + auto* btnLayout = new QHBoxLayout(); btnLayout->setSpacing(8); + addDllBtn = new QPushButton("Add DLL", this); addDllBtn->setMaximumWidth(100); addDllBtn->setMinimumHeight(26); @@ -93,6 +105,7 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "}" ); btnLayout->addWidget(addDllBtn); + removeDllBtn = new QPushButton("Remove", this); removeDllBtn->setMaximumWidth(80); removeDllBtn->setMinimumHeight(26); @@ -114,9 +127,11 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "}" ); btnLayout->addWidget(removeDllBtn); + headerBtnLayout->addLayout(btnLayout); dllLayout->addLayout(headerBtnLayout); dllLayout->addSpacing(3); + dllList = new QListWidget(this); dllList->setStyleSheet( "QListWidget {" @@ -141,24 +156,27 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { ); dllList->setFixedHeight(90); dllList->setContextMenuPolicy(Qt::CustomContextMenu); + dllLayout->addWidget(dllList); dllFrame->setFixedHeight(175); layout->addWidget(dllFrame); layout->addSpacing(15); auto* buttonsSection = new QVBoxLayout(); buttonsSection->setSpacing(10); + validateBtn = new BindButton( - " Validation Check", - ":/src/Res/Icons/validation.svg", + " Validation Check", + ":/src/Res/Icons/validation.png", "Bind Validation", "Analyzes and updates syscall offsets in syscaller.asm by comparing against ntdll.dll.

" "• Disassembles ntdll.dll exports to extract syscall IDs and ensures correct mapping
" "• Updates or removes syscalls based on their presence in the current systems ntdll.dll" ); buttonsSection->addWidget(validateBtn); + compatibilityBtn = new BindButton( - " Compatibility Check", - ":/src/Res/Icons/compatibility.svg", + " Compatibility Check", + ":/src/Res/Icons/compatibility.png", "Bind Compatibility", "Performs compatibility analysis of syscalls against ntdll.dll:

" "• Detects duplicate syscall names and offsets
" @@ -167,9 +185,10 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "• Reports valid, invalid, and duplicate syscalls with detailed status" ); buttonsSection->addWidget(compatibilityBtn); + verifyBtn = new BindButton( - " Verification Check", - ":/src/Res/Icons/verification.svg", + " Verification Check", + ":/src/Res/Icons/verification.png", "Bind Verification", "Performs comprehensive syscall verification:

" "• Validates return types (NTSTATUS, BOOL, HANDLE, etc.)
" @@ -178,9 +197,10 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "• Traces type definitions in header files" ); buttonsSection->addWidget(verifyBtn); + obfuscateBtn = new BindButton( - " Obfuscation", - ":/src/Res/Icons/obfuscation.svg", + " Obfuscation", + ":/src/Res/Icons/obfuscation.png", "Bind Obfuscation", "Obfuscates syscalls to enhance protection:

" "• Randomizes syscall names and offsets
" @@ -189,13 +209,15 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "• Preserves original syscall functionality" ); buttonsSection->addWidget(obfuscateBtn); + settingsBtn = new BindButton( " Settings", - ":/src/Res/Icons/settings.svg", + ":/src/Res/Icons/settings.png", "Bind Settings", "Configure SysCaller project settings" ); buttonsSection->addWidget(settingsBtn); + layout->addLayout(buttonsSection); layout->addSpacing(10); auto* statusFrame = new QFrame(this); @@ -206,21 +228,28 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { " padding: 10px;" "}" ); + auto* statusLayout = new QVBoxLayout(statusFrame); statusLayout->setContentsMargins(15, 15, 15, 15); + progressBar = new ProgressBar(this); statusLayout->addWidget(progressBar); + statusLabel = new QLabel("Ready", this); statusLabel->setStyleSheet( "color: #666666; font-size: 12px; padding: 5px; border-radius: 5px; background: rgba(102, 102, 102, 0.1);" ); statusLabel->setAlignment(Qt::AlignCenter); statusLayout->addWidget(statusLabel); + layout->addWidget(statusFrame); layout->addStretch(); + dllList->addItem("C:\\Windows\\System32\\ntdll.dll"); + connect(addDllBtn, &QPushButton::clicked, this, &LeftPanel::browseDll); emit dllPathsChanged(getDllPaths()); + connect(removeDllBtn, &QPushButton::clicked, this, &LeftPanel::removeSelectedDll); connect(dllList, &QListWidget::customContextMenuRequested, this, &LeftPanel::showContextMenu); connect(settingsBtn, &BindButton::clicked, this, &LeftPanel::settingsButtonClicked); @@ -231,43 +260,60 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { connect(versionLabel, &QLabel::linkActivated, this, &LeftPanel::showChangelogDialog); } -void LeftPanel::browseDll() { +void LeftPanel::browseDll() +{ QString dllPath = QFileDialog::getOpenFileName( this, - "Bind - v1.3.0", + "Bind - v1.3.1", "", "DLL Files (*.dll);;All Files (*.*)" ); - if (!dllPath.isEmpty()) { + + if (!dllPath.isEmpty()) + { bool exists = false; - for (int i = 0; i < dllList->count(); ++i) { - if (dllList->item(i)->text() == dllPath) { + + for (int i = 0; i < dllList->count(); ++i) + { + if (dllList->item(i)->text() == dllPath) + { exists = true; break; } } - if (!exists) { + + if (!exists) + { dllList->addItem(dllPath); emit dllPathsChanged(getDllPaths()); } } } -void LeftPanel::removeSelectedDll() { +void LeftPanel::removeSelectedDll() +{ QList selectedItems = dllList->selectedItems(); - if (selectedItems.isEmpty() || dllList->count() <= 1) { + + if (selectedItems.isEmpty() || dllList->count() <= 1) + { return; } - for (QListWidgetItem* item : selectedItems) { + + for (QListWidgetItem* item : selectedItems) + { delete dllList->takeItem(dllList->row(item)); } + emit dllPathsChanged(getDllPaths()); } -void LeftPanel::showContextMenu(const QPoint& pos) { - if (dllList->count() <= 1) { +void LeftPanel::showContextMenu(const QPoint& pos) +{ + if (dllList->count() <= 1) + { return; } + QMenu menu(this); menu.setStyleSheet( "QMenu {" @@ -282,35 +328,48 @@ void LeftPanel::showContextMenu(const QPoint& pos) { " background-color: #4880a8;" "}" ); + QAction* removeAction = menu.addAction("Remove"); QAction* selectedAction = menu.exec(dllList->mapToGlobal(pos)); - if (selectedAction == removeAction) { + + if (selectedAction == removeAction) + { removeSelectedDll(); } } -QStringList LeftPanel::getDllPaths() const { +QStringList LeftPanel::getDllPaths() const +{ QStringList paths; - for (int i = 0; i < dllList->count(); ++i) { + + for (int i = 0; i < dllList->count(); ++i) + { paths.append(dllList->item(i)->text()); } + return paths; } -void LeftPanel::setProgressIndeterminate(bool indeterminate) { - if (indeterminate) { +void LeftPanel::setProgressIndeterminate(bool indeterminate) +{ + if (indeterminate) + { progressBar->setRange(0, 0); - } else { + } + else + { progressBar->setRange(0, 1); progressBar->setValue(1); } } -void LeftPanel::updateStatus(const QString& message) { +void LeftPanel::updateStatus(const QString& message) +{ statusLabel->setText(message); } -void LeftPanel::showChangelogDialog() { +void LeftPanel::showChangelogDialog() +{ ChangelogDialog dialog(this); dialog.exec(); } diff --git a/Bind/src/GUI/Panels/OutputPanel.cpp b/Bind/src/GUI/Panels/OutputPanel.cpp index bf6d971..9354583 100644 --- a/Bind/src/GUI/Panels/OutputPanel.cpp +++ b/Bind/src/GUI/Panels/OutputPanel.cpp @@ -1,6 +1,8 @@ #include "include/GUI/Panels/OutputPanel.h" -OutputPanel::OutputPanel(QWidget* parent) : QTextEdit(parent) { +OutputPanel::OutputPanel(QWidget* parent) + : QTextEdit(parent) +{ setReadOnly(true); setStyleSheet( "QTextEdit {" @@ -13,13 +15,15 @@ OutputPanel::OutputPanel(QWidget* parent) : QTextEdit(parent) { ); } -void OutputPanel::appendText(const QString& text) { +void OutputPanel::appendText(const QString& text) +{ append(text); QTextCursor cursor = textCursor(); cursor.movePosition(QTextCursor::End); setTextCursor(cursor); } -void OutputPanel::clearText() { +void OutputPanel::clearText() +{ clear(); } \ No newline at end of file diff --git a/Bind/src/GUI/Panels/RightPanel.cpp b/Bind/src/GUI/Panels/RightPanel.cpp index e7ed3c6..7e54d9b 100644 --- a/Bind/src/GUI/Panels/RightPanel.cpp +++ b/Bind/src/GUI/Panels/RightPanel.cpp @@ -3,28 +3,35 @@ #include #include -RightPanel::RightPanel(QWidget* parent) : QFrame(parent) { +RightPanel::RightPanel(QWidget* parent) + : QFrame(parent) +{ setStyleSheet( "QFrame {" " background: #252525;" " border-radius: 15px;" "}" ); + auto* layout = new QVBoxLayout(this); layout->setContentsMargins(20, 20, 20, 20); + headerLabel = new QLabel("Bind Console", this); headerLabel->setStyleSheet( "color: #0077d4; font-size: 16px; font-weight: bold; padding: 10px; background: rgba(72, 128, 168, 0.2); border-radius: 8px;" ); layout->addWidget(headerLabel); + outputText = new OutputPanel(this); layout->addWidget(outputText); } -void RightPanel::appendOutput(const QString& text) { +void RightPanel::appendOutput(const QString& text) +{ outputText->appendText(text); } -void RightPanel::clearOutput() { +void RightPanel::clearOutput() +{ outputText->clearText(); } \ No newline at end of file diff --git a/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp b/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp index 92682ef..c8da96a 100644 --- a/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp @@ -12,44 +12,56 @@ #include #include -GeneralTab::GeneralTab(QSettings* settings, QWidget* parent) - : QWidget(parent), - settings(settings), - modeButtonGroup(nullptr), - ntModeRadio(nullptr), - zwModeRadio(nullptr), - bindingsButtonGroup(nullptr), - bindingsEnableRadio(nullptr), - bindingsDisableRadio(nullptr), - bindingsGroup(nullptr), - inlineAssemblyGroup(nullptr), - assemblyModeGroup(nullptr), - directAssemblyRadio(nullptr), - inlineAssemblyRadio(nullptr), - indirectAssemblyRadio(nullptr), - hashStubs(nullptr), - createBackup(nullptr) { +GeneralTab::GeneralTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) + , modeButtonGroup(nullptr) + , ntModeRadio(nullptr) + , zwModeRadio(nullptr) + , bindingsButtonGroup(nullptr) + , bindingsEnableRadio(nullptr) + , bindingsDisableRadio(nullptr) + , bindingsGroup(nullptr) + , inlineAssemblyGroup(nullptr) + , assemblyModeGroup(nullptr) + , directAssemblyRadio(nullptr) + , inlineAssemblyRadio(nullptr) + , indirectAssemblyRadio(nullptr) + , hashStubs(nullptr) + , createBackup(nullptr) +{ initUI(); } -void GeneralTab::initUI() { +void GeneralTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); QGroupBox* syscallModeGroup = new QGroupBox("Syscall Mode"); QVBoxLayout* syscallModeLayout = new QVBoxLayout(); + QLabel* description = new QLabel("Select which syscall mode to use. This affects how syscalls are generated and processed."); description->setWordWrap(true); syscallModeLayout->addWidget(description); + modeButtonGroup = new QButtonGroup(this); + ntModeRadio = new QRadioButton("Nt Mode (User Mode)"); ntModeRadio->setToolTip("Use Nt prefix for syscalls (default for user-mode applications)"); + zwModeRadio = new QRadioButton("Zw Mode (Kernel Mode)"); zwModeRadio->setToolTip("Use Zw prefix for syscalls (primarily used in kernel-mode drivers)"); + originalMode = settings->value("general/syscall_mode", "Nt").toString(); - if (originalMode == "Zw") { + + if (originalMode == "Zw") + { zwModeRadio->setChecked(true); - } else { + } + else + { ntModeRadio->setChecked(true); } + modeButtonGroup->addButton(ntModeRadio); modeButtonGroup->addButton(zwModeRadio); syscallModeLayout->addWidget(ntModeRadio); @@ -58,58 +70,83 @@ void GeneralTab::initUI() { layout->addWidget(syscallModeGroup); connect(ntModeRadio, &QRadioButton::toggled, this, &GeneralTab::onModeChanged); connect(zwModeRadio, &QRadioButton::toggled, this, &GeneralTab::onModeChanged); + bindingsGroup = new QGroupBox("Bindings"); QVBoxLayout* bindingsLayout = new QVBoxLayout(); + QLabel* bindingsDesc = new QLabel("Enable or disable automatic generation of SysCaller.def for bindings."); bindingsDesc->setWordWrap(true); bindingsLayout->addWidget(bindingsDesc); + bindingsButtonGroup = new QButtonGroup(this); + bindingsEnableRadio = new QRadioButton("Enable"); bindingsDisableRadio = new QRadioButton("Disable"); + bindingsButtonGroup->addButton(bindingsEnableRadio); bindingsButtonGroup->addButton(bindingsDisableRadio); bindingsLayout->addWidget(bindingsEnableRadio); bindingsLayout->addWidget(bindingsDisableRadio); bindingsGroup->setLayout(bindingsLayout); layout->addWidget(bindingsGroup); + bool bindingsEnabled = settings->value("general/bindings_enabled", false).toBool(); - if (bindingsEnabled) { + + if (bindingsEnabled) + { bindingsEnableRadio->setChecked(true); - } else { + } + else + { bindingsDisableRadio->setChecked(true); } - inlineAssemblyGroup = new QGroupBox("Assembly Mode"); + inlineAssemblyGroup = new QGroupBox("Syscall Mode"); QVBoxLayout* inlineAssemblyLayout = new QVBoxLayout(); - QLabel* inlineDesc = new QLabel("Select the assembly mode for syscall generation."); + + QLabel* inlineDesc = new QLabel("Select a mode for syscall generation."); inlineDesc->setWordWrap(true); inlineAssemblyLayout->addWidget(inlineDesc); + QButtonGroup* assemblyModeGroup = new QButtonGroup(this); - QRadioButton* directAssemblyRadio = new QRadioButton("Direct Assembly Mode (Default)"); + + QRadioButton* directAssemblyRadio = new QRadioButton("Direct Syscall Mode (Default)"); directAssemblyRadio->setToolTip("Use traditional instruction mnemonics for syscall stubs"); - QRadioButton* inlineAssemblyRadio = new QRadioButton("Inline Assembly Mode"); + + QRadioButton* inlineAssemblyRadio = new QRadioButton("Inline Syscall Mode"); inlineAssemblyRadio->setToolTip("Generate MASM compatible db-based stubs"); - QRadioButton* indirectAssemblyRadio = new QRadioButton("Indirect Assembly Mode"); + + QRadioButton* indirectAssemblyRadio = new QRadioButton("Indirect Syscall Mode"); indirectAssemblyRadio->setToolTip("Generate runtime syscall resolution stubs for enhanced stealth"); + assemblyModeGroup->addButton(directAssemblyRadio); assemblyModeGroup->addButton(inlineAssemblyRadio); assemblyModeGroup->addButton(indirectAssemblyRadio); + bool inlineEnabled = settings->value("general/inline_assembly", false).toBool(); bool indirectEnabled = settings->value("general/indirect_assembly", false).toBool(); - - if (inlineEnabled) { + + if (inlineEnabled) + { inlineAssemblyRadio->setChecked(true); - } else if (indirectEnabled) { + } + else if (indirectEnabled) + { indirectAssemblyRadio->setChecked(true); - } else { + } + else + { directAssemblyRadio->setChecked(true); } + inlineAssemblyLayout->addWidget(directAssemblyRadio); inlineAssemblyLayout->addWidget(inlineAssemblyRadio); inlineAssemblyLayout->addWidget(indirectAssemblyRadio); + this->directAssemblyRadio = directAssemblyRadio; this->inlineAssemblyRadio = inlineAssemblyRadio; this->indirectAssemblyRadio = indirectAssemblyRadio; this->assemblyModeGroup = assemblyModeGroup; + connect(directAssemblyRadio, &QRadioButton::toggled, this, &GeneralTab::onAssemblyModeChanged); connect(inlineAssemblyRadio, &QRadioButton::toggled, this, &GeneralTab::onAssemblyModeChanged); connect(indirectAssemblyRadio, &QRadioButton::toggled, this, &GeneralTab::onAssemblyModeChanged); @@ -117,94 +154,147 @@ void GeneralTab::initUI() { layout->addWidget(inlineAssemblyGroup); QGroupBox* hashStubsGroup = new QGroupBox("Hash Stubs"); QVBoxLayout* hashStubsLayout = new QVBoxLayout(); + QLabel* hashDesc = new QLabel("Optionally hash each stub/build with unique hash for future lookups."); hashDesc->setWordWrap(true); hashStubsLayout->addWidget(hashDesc); + QPushButton* hashCompareBtn = new QPushButton("Hash Compare"); hashCompareBtn->setToolTip("Compare hash files from different builds to identify changes"); connect(hashCompareBtn, &QPushButton::clicked, this, &GeneralTab::openHashCompare); hashStubsLayout->addWidget(hashCompareBtn); + hashStubs = new QCheckBox("Enable Hash Stubs"); hashStubs->setChecked(settings->value("general/hash_stubs", false).toBool()); hashStubs->setToolTip("If checked, will generate hashes for all stubs after obfuscation and save them to a JSON file in the Backups directory"); hashStubsLayout->addWidget(hashStubs); hashStubsGroup->setLayout(hashStubsLayout); layout->addWidget(hashStubsGroup); + QGroupBox* resetGroup = new QGroupBox("Reset to Default/Backup"); QVBoxLayout* resetLayout = new QVBoxLayout(); + QLabel* resetDesc = new QLabel("Reset SysCaller to it's default state or restore from a backup. This will revert any changes made by obfuscation or manual editing."); resetDesc->setWordWrap(true); resetLayout->addWidget(resetDesc); + QPushButton* restoreBtn = new QPushButton("Restore Files"); restoreBtn->setMinimumHeight(40); connect(restoreBtn, &QPushButton::clicked, this, &GeneralTab::showRestoreOptions); resetLayout->addWidget(restoreBtn); + createBackup = new QCheckBox("Create Backup"); createBackup->setChecked(settings->value("general/create_backup", true).toBool()); createBackup->setToolTip("If checked, will create backup files in the Backups directory before restoring defaults"); resetLayout->addWidget(createBackup); resetGroup->setLayout(resetLayout); layout->addWidget(resetGroup); + onModeChanged(); } -void GeneralTab::onModeChanged() { +void GeneralTab::onModeChanged() +{ bool isKernelMode = zwModeRadio->isChecked(); bindingsGroup->setVisible(!isKernelMode); inlineAssemblyGroup->setVisible(!isKernelMode); - if (isKernelMode && bindingsEnableRadio->isChecked()) { + + if (isKernelMode && bindingsEnableRadio->isChecked()) + { bindingsDisableRadio->setChecked(true); } - if (isKernelMode) { - if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) { + + if (isKernelMode) + { + if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) + { directAssemblyRadio->setChecked(true); } - if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) { + + if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) + { directAssemblyRadio->setChecked(true); } - if (inlineAssemblyRadio) inlineAssemblyRadio->setEnabled(false); - if (indirectAssemblyRadio) indirectAssemblyRadio->setEnabled(false); - if (directAssemblyRadio) directAssemblyRadio->setEnabled(true); - } else { - if (inlineAssemblyRadio) inlineAssemblyRadio->setEnabled(true); - if (indirectAssemblyRadio) indirectAssemblyRadio->setEnabled(true); - if (directAssemblyRadio) directAssemblyRadio->setEnabled(true); + + if (inlineAssemblyRadio) + { + inlineAssemblyRadio->setEnabled(false); + } + + if (indirectAssemblyRadio) + { + indirectAssemblyRadio->setEnabled(false); + } + + if (directAssemblyRadio) + { + directAssemblyRadio->setEnabled(true); + } + } + else + { + if (inlineAssemblyRadio) + { + inlineAssemblyRadio->setEnabled(true); + } + + if (indirectAssemblyRadio) + { + indirectAssemblyRadio->setEnabled(true); + } + + if (directAssemblyRadio) + { + directAssemblyRadio->setEnabled(true); + } } } void GeneralTab::onAssemblyModeChanged() { } -void GeneralTab::saveSettings() { +void GeneralTab::saveSettings() +{ settings->setValue("general/create_backup", createBackup->isChecked()); settings->setValue("general/hash_stubs", hashStubs->isChecked()); settings->setValue("general/bindings_enabled", bindingsEnableRadio->isChecked()); - if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) { + + if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) + { settings->setValue("general/inline_assembly", true); settings->setValue("general/indirect_assembly", false); - } else if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) { + } + else if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) + { settings->setValue("general/inline_assembly", false); settings->setValue("general/indirect_assembly", true); - } else { + } + else + { settings->setValue("general/inline_assembly", false); settings->setValue("general/indirect_assembly", false); } + QString newMode = zwModeRadio->isChecked() ? "Zw" : "Nt"; bool modeChanged = newMode != originalMode; settings->setValue("general/syscall_mode", newMode); - if (modeChanged) { - ConfirmationDialog infoDialog("Bind - v1.3.0", this); + + if (modeChanged) + { + ConfirmationDialog infoDialog("Bind - v1.3.1", this); infoDialog.setMessage(QString("The syscall mode has been changed from %1 to %2.\n\n" "This change affects which files are processed:\n" "- Nt Mode: User mode files in SysCaller directory\n" "- Zw Mode: Kernel mode files in SysCallerK directory\n\n" - "Some changes may take full effect after a restart.").arg(originalMode, newMode)); + "Some changes may take full effect after a restart.") + .arg(originalMode, newMode)); infoDialog.setButtons(false, false, true, false); infoDialog.exec(); } } -void GeneralTab::showRestoreOptions() { +void GeneralTab::showRestoreOptions() +{ QMenu menu(this); menu.setStyleSheet( "QMenu {" @@ -223,82 +313,117 @@ void GeneralTab::showRestoreOptions() { " background-color: #0b5394;" "}" ); + QAction* defaultAction = menu.addAction("Restore Default Files"); connect(defaultAction, &QAction::triggered, this, &GeneralTab::restoreDefaultFiles); + QStringList completeBackups = getAvailableBackups(); - if (!completeBackups.isEmpty()) { - if (completeBackups.size() > 1) { + + if (!completeBackups.isEmpty()) + { + if (completeBackups.size() > 1) + { QString latestTs = completeBackups.first(); QString latestDate = formatTimestamp(latestTs); + QAction* latestAction = menu.addAction( QString("Restore Latest Backup (%1)").arg(latestDate)); - connect(latestAction, &QAction::triggered, [this, latestTs]() { + connect(latestAction, &QAction::triggered, [this, latestTs]() + { restoreBackup(latestTs); }); + QMenu* backupSubmenu = new QMenu("Select Backup", &menu); backupSubmenu->setStyleSheet(menu.styleSheet()); - for (const QString& ts : completeBackups) { + + for (const QString& ts : completeBackups) + { QString dateStr = formatTimestamp(ts); QAction* action = backupSubmenu->addAction(QString("Backup from %1").arg(dateStr)); - connect(action, &QAction::triggered, [this, ts]() { + connect(action, &QAction::triggered, [this, ts]() + { restoreBackup(ts); }); } + menu.addMenu(backupSubmenu); - } else { + } + else + { QString ts = completeBackups.first(); QString dateStr = formatTimestamp(ts); + QAction* backupAction = menu.addAction( QString("Restore Backup (%1)").arg(dateStr)); - connect(backupAction, &QAction::triggered, [this, ts]() { + connect(backupAction, &QAction::triggered, [this, ts]() + { restoreBackup(ts); }); } - } else { + } + else + { QAction* backupAction = menu.addAction("No Backups Available"); backupAction->setEnabled(false); } + menu.exec(QCursor::pos()); } -void GeneralTab::restoreDefaultFiles() { +void GeneralTab::restoreDefaultFiles() +{ bool isKernelMode = settings->value("general/syscall_mode", "Nt").toString() == "Zw"; QString modeText = isKernelMode ? "kernel mode" : "user mode"; QString filePathText = isKernelMode ? "SysCallerK directory" : "SysCaller directory"; QString headerName = isKernelMode ? "sysFunctions_k.h" : "sysFunctions.h"; - ConfirmationDialog confirmDialog("Bind - v1.3.0", this); + + ConfirmationDialog confirmDialog("Bind - v1.3.1", this); confirmDialog.setMessage(QString("Are you sure you want to restore default %1 files?\n\n" "This will overwrite your current syscaller.asm and %2 files in the %3.") .arg(modeText, headerName, filePathText)); - if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) { + if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) + { return; } - try { + + try + { QString defaultAsmPath = PathUtils::getDefaultSysCallerAsmPath(); QString defaultHeaderPath = PathUtils::getDefaultSysFunctionsPath(isKernelMode); QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); - if (!QFile::exists(defaultAsmPath) || !QFile::exists(defaultHeaderPath)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (!QFile::exists(defaultAsmPath) || !QFile::exists(defaultHeaderPath)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("Default files not found in Default directory."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - if (createBackup->isChecked()) { + + if (createBackup->isChecked()) + { createBackupFiles(); } - if (QFile::exists(asmPath)) { + + if (QFile::exists(asmPath)) + { QFile::remove(asmPath); } - if (QFile::exists(headerPath)) { + + if (QFile::exists(headerPath)) + { QFile::remove(headerPath); } + bool asmCopied = QFile::copy(defaultAsmPath, asmPath); bool headerCopied = QFile::copy(defaultHeaderPath, headerPath); - if (!asmCopied || !headerCopied) { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + + if (!asmCopied || !headerCopied) + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage(QString("Failed to copy files:\nASM: %1\nHeader: %2") .arg(asmCopied ? "Success" : "Failed") .arg(headerCopied ? "Success" : "Failed")); @@ -306,235 +431,337 @@ void GeneralTab::restoreDefaultFiles() { errorDialog.exec(); return; } - ConfirmationDialog infoDialog("Bind - v1.3.0", this); + + ConfirmationDialog infoDialog("Bind - v1.3.1", this); infoDialog.setMessage(QString("Default %1 files have been restored successfully!").arg(modeText)); infoDialog.setButtons(false, false, true, false); infoDialog.exec(); - } catch (...) { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + } + catch (...) + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage("An error occurred while restoring default files."); errorDialog.setButtons(false, true, false); errorDialog.exec(); } } -void GeneralTab::restoreBackup(const QString& timestamp) { - try { +void GeneralTab::restoreBackup(const QString& timestamp) +{ + try + { QString backupsDir = PathUtils::getBackupsPath(); QStringList completeBackups = getAvailableBackups(); - if (!completeBackups.contains(timestamp)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (!completeBackups.contains(timestamp)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage(QString("Could not find complete backup set for timestamp %1").arg(timestamp)); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } + QString backupAsmPath = QString("%1/syscaller_%2.asm").arg(backupsDir, timestamp); QString backupHeaderPath = QString("%1/sysFunctions_%2.h").arg(backupsDir, timestamp); QStringList missingFiles; - if (!QFile::exists(backupAsmPath)) { + + if (!QFile::exists(backupAsmPath)) + { missingFiles << QString("ASM file: %1").arg(backupAsmPath); } - if (!QFile::exists(backupHeaderPath)) { + + if (!QFile::exists(backupHeaderPath)) + { missingFiles << QString("Header file: %1").arg(backupHeaderPath); } - if (!missingFiles.isEmpty()) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (!missingFiles.isEmpty()) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage(QString("Could not find the following backup files:\n%1").arg(missingFiles.join("\n"))); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - ConfirmationDialog confirmDialog("Bind - v1.3.0", this); + + ConfirmationDialog confirmDialog("Bind - v1.3.1", this); confirmDialog.setMessage(QString("Are you sure you want to restore from backup files dated %1?\n\n" "This will overwrite your current syscaller.asm and sysFunctions.h files.") .arg(formatTimestamp(timestamp))); - if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) { + if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) + { return; } + bool isKernelMode = settings->value("general/syscall_mode", "Nt").toString() == "Zw"; QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); + QDir().mkpath(QFileInfo(asmPath).absolutePath()); QDir().mkpath(QFileInfo(headerPath).absolutePath()); - if (QFile::exists(asmPath) && isFileLocked(asmPath)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (QFile::exists(asmPath) && isFileLocked(asmPath)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("The ASM file appears to be locked by another process. Close any applications that might be using it and try again."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - if (QFile::exists(headerPath) && isFileLocked(headerPath)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (QFile::exists(headerPath) && isFileLocked(headerPath)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("The header file appears to be locked by another process. Close any applications that might be using it and try again."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - if (createBackup->isChecked()) { + + if (createBackup->isChecked()) + { createBackupFiles(); } + bool asmRestored = false; bool headerRestored = false; + asmRestored = restoreFileWithRetry(backupAsmPath, asmPath, "ASM"); - if (!asmRestored) { - if (QFile::exists(asmPath)) { + + if (!asmRestored) + { + if (QFile::exists(asmPath)) + { QFile::remove(asmPath); } + asmRestored = QFile::copy(backupAsmPath, asmPath); } + headerRestored = restoreFileWithRetry(backupHeaderPath, headerPath, "Header"); - if (!headerRestored) { - if (QFile::exists(headerPath)) { + + if (!headerRestored) + { + if (QFile::exists(headerPath)) + { QFile::remove(headerPath); } + headerRestored = QFile::copy(backupHeaderPath, headerPath); } - if (asmRestored && headerRestored) { - ConfirmationDialog infoDialog("Bind - v1.3.0", this); + + if (asmRestored && headerRestored) + { + ConfirmationDialog infoDialog("Bind - v1.3.1", this); infoDialog.setMessage(QString("Files have been restored from backup successfully!\n\nBackup date: %1") .arg(formatTimestamp(timestamp))); infoDialog.setButtons(false, false, true, false); infoDialog.exec(); - } else if (!asmRestored && headerRestored) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + } + else if (!asmRestored && headerRestored) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("Only the header file was restored successfully. The ASM file could not be restored."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); - } else if (asmRestored && !headerRestored) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + } + else if (asmRestored && !headerRestored) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("Only the ASM file was restored successfully. The header file could not be restored."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); - } else { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + } + else + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage("Failed to restore both files from backup."); errorDialog.setButtons(false, false, true, false); errorDialog.exec(); } - } catch (...) { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + } + catch (...) + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage("An error occurred while restoring backup files."); errorDialog.setButtons(false, true, false); errorDialog.exec(); } } -void GeneralTab::openHashCompare() { +void GeneralTab::openHashCompare() +{ HashCompareDialog dialog(this); dialog.exec(); } -QString GeneralTab::formatTimestamp(const QString& timestamp) { - if (timestamp.contains('_')) { +QString GeneralTab::formatTimestamp(const QString& timestamp) +{ + if (timestamp.contains('_')) + { QStringList parts = timestamp.split('_'); - if (parts.size() >= 2) { + + if (parts.size() >= 2) + { QString datePart = parts[0]; QString timePart = parts[1]; - if (datePart.length() >= 8 && timePart.length() >= 4) { + + if (datePart.length() >= 8 && timePart.length() >= 4) + { QString year = datePart.mid(0, 4); QString month = datePart.mid(4, 2); QString day = datePart.mid(6, 2); QString hour = timePart.mid(0, 2); QString minute = timePart.mid(2, 2); + return QString("%1-%2-%3 %4:%5").arg(year, month, day, hour, minute); } } } + return timestamp; } -QString GeneralTab::getIniPath() { +QString GeneralTab::getIniPath() +{ return PathUtils::getIniPath(); } -bool GeneralTab::isFileLocked(const QString& filePath) { - if (!QFile::exists(filePath)) { +bool GeneralTab::isFileLocked(const QString& filePath) +{ + if (!QFile::exists(filePath)) + { return false; } + QFile file(filePath); - if (!file.open(QIODevice::ReadWrite)) { + + if (!file.open(QIODevice::ReadWrite)) + { return true; } + file.close(); return false; } -QStringList GeneralTab::getAvailableBackups() { +QStringList GeneralTab::getAvailableBackups() +{ QStringList completeBackups; QString backupsDir = PathUtils::getBackupsPath(); QDir dir(backupsDir); - if (!dir.exists()) { + + if (!dir.exists()) + { return completeBackups; } + QStringList filters; filters << "syscaller_*.asm"; + QFileInfoList asmFiles = dir.entryInfoList(filters, QDir::Files); - for (const QFileInfo& asmFile : asmFiles) { + + for (const QFileInfo& asmFile : asmFiles) + { QString fileName = asmFile.fileName(); - if (fileName.startsWith("syscaller_") && fileName.endsWith(".asm")) { + + if (fileName.startsWith("syscaller_") && fileName.endsWith(".asm")) + { QString timestamp = fileName.mid(10, fileName.length() - 14); QString headerFile = QString("sysFunctions_%1.h").arg(timestamp); QString headerPath = QString("%1/%2").arg(backupsDir, headerFile); - if (QFile::exists(headerPath)) { + + if (QFile::exists(headerPath)) + { completeBackups.append(timestamp); } } } + std::sort(completeBackups.begin(), completeBackups.end(), std::greater()); return completeBackups; } -void GeneralTab::createBackupFiles() { - try { +void GeneralTab::createBackupFiles() +{ + try + { QString backupsDir = PathUtils::getBackupsPath(); QDir().mkpath(backupsDir); + QDateTime now = QDateTime::currentDateTime(); QString timestamp = now.toString("yyyyMMdd_HHmmss"); + bool isKernelMode = settings->value("general/syscall_mode", "Nt").toString() == "Zw"; QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); + QString backupAsmPath = QString("%1/syscaller_%2.asm").arg(backupsDir, timestamp); QString backupHeaderPath = QString("%1/sysFunctions_%2.h").arg(backupsDir, timestamp); - if (QFile::exists(asmPath)) { + + if (QFile::exists(asmPath)) + { QFile::copy(asmPath, backupAsmPath); } - if (QFile::exists(headerPath)) { + + if (QFile::exists(headerPath)) + { QFile::copy(headerPath, backupHeaderPath); } - } catch (...) { + } + catch (...) + { // backup creation failed but dont stop the operation } } -bool GeneralTab::restoreFileWithRetry(const QString& sourcePath, const QString& destPath, const QString& fileType) { +bool GeneralTab::restoreFileWithRetry(const QString& sourcePath, const QString& destPath, const QString& fileType) +{ const int maxRetries = 3; int retryCount = 0; - while (retryCount < maxRetries) { - try { - if (QFile::exists(destPath)) { + + while (retryCount < maxRetries) + { + try + { + if (QFile::exists(destPath)) + { QFile::remove(destPath); } - if (QFile::copy(sourcePath, destPath)) { - if (QFile::exists(destPath)) { + + if (QFile::copy(sourcePath, destPath)) + { + if (QFile::exists(destPath)) + { QFileInfo destInfo(destPath); QFileInfo sourceInfo(sourcePath); - if (destInfo.size() > 0 && destInfo.size() == sourceInfo.size()) { + + if (destInfo.size() > 0 && destInfo.size() == sourceInfo.size()) + { return true; } } } + retryCount++; - if (retryCount < maxRetries) { + + if (retryCount < maxRetries) + { QThread::msleep(1000); } - } catch (...) { + } + catch (...) + { retryCount++; - if (retryCount < maxRetries) { + + if (retryCount < maxRetries) + { QThread::msleep(1000); } } } + return false; } diff --git a/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp b/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp index b600fe4..eaea4d2 100644 --- a/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp @@ -3,32 +3,55 @@ #include #include -IndirectObfuscationTab::IndirectObfuscationTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +IndirectObfuscationTab::IndirectObfuscationTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void IndirectObfuscationTab::initUI() { +void IndirectObfuscationTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); setupJunkInstructionsGroup(); setupResolverObfuscationGroup(); setupEncryptionGroup(); setupControlFlowGroup(); - if (junkGroup) layout->addWidget(junkGroup); - if (resolverGroup) layout->addWidget(resolverGroup); - if (encryptionGroup) layout->addWidget(encryptionGroup); - if (controlFlowGroup) layout->addWidget(controlFlowGroup); + + if (junkGroup) + { + layout->addWidget(junkGroup); + } + + if (resolverGroup) + { + layout->addWidget(resolverGroup); + } + + if (encryptionGroup) + { + layout->addWidget(encryptionGroup); + } + + if (controlFlowGroup) + { + layout->addWidget(controlFlowGroup); + } + loadSettings(); } -void IndirectObfuscationTab::setupJunkInstructionsGroup() { +void IndirectObfuscationTab::setupJunkInstructionsGroup() +{ junkGroup = new QGroupBox("Indirect Junk Instructions"); QFormLayout* junkLayout = new QFormLayout(); + indirectMinInstructions = new QSpinBox(); indirectMinInstructions->setRange(1, 10); indirectMinInstructions->setValue(settings->value("obfuscation/indirect_min_instructions", 2).toInt()); indirectMinInstructions->setToolTip("Minimum number of junk instructions to add to indirect stubs"); junkLayout->addRow("Minimum Instructions:", indirectMinInstructions); + indirectMaxInstructions = new QSpinBox(); indirectMaxInstructions->setRange(1, 20); indirectMaxInstructions->setValue(settings->value("obfuscation/indirect_max_instructions", 8).toInt()); @@ -37,26 +60,34 @@ void IndirectObfuscationTab::setupJunkInstructionsGroup() { junkGroup->setLayout(junkLayout); } -void IndirectObfuscationTab::setupResolverObfuscationGroup() { +void IndirectObfuscationTab::setupResolverObfuscationGroup() +{ resolverGroup = new QGroupBox("Resolver Obfuscation"); QFormLayout* resolverLayout = new QFormLayout(); + indirectObfuscateCalls = new QCheckBox("Obfuscate Resolver Calls"); indirectObfuscateCalls->setChecked(settings->value("obfuscation/indirect_obfuscate_calls", true).toBool()); indirectObfuscateCalls->setToolTip("Obfuscate calls to the syscall resolver function using function pointers"); resolverLayout->addRow(indirectObfuscateCalls); + indirectResolverMethod = new QComboBox(); indirectResolverMethod->addItem("Random Pattern", "random"); indirectResolverMethod->addItem("Register Based (Safest)", "register"); indirectResolverMethod->addItem("Stack Based (Aligned)", "stack"); indirectResolverMethod->addItem("Indirect Data", "indirect"); indirectResolverMethod->addItem("Register Shuffle", "shuffle"); + QString savedMethod = settings->value("obfuscation/indirect_resolver_method", "random").toString(); int index = indirectResolverMethod->findData(savedMethod); - if (index >= 0) { + + if (index >= 0) + { indirectResolverMethod->setCurrentIndex(index); } + indirectResolverMethod->setToolTip("Choose the function pointer obfuscation method for resolver calls"); resolverLayout->addRow("Resolver Method:", indirectResolverMethod); + indirectEncryptStrings = new QCheckBox("Encrypt Nt* Resolver Names"); indirectEncryptStrings->setChecked(settings->value("obfuscation/indirect_encrypt_strings", false).toBool()); indirectEncryptStrings->setToolTip("Encrypt strings like NtOpenProcess and decrypt them on the stack at runtime before calling the resolver"); @@ -64,9 +95,11 @@ void IndirectObfuscationTab::setupResolverObfuscationGroup() { resolverGroup->setLayout(resolverLayout); } -void IndirectObfuscationTab::setupEncryptionGroup() { +void IndirectObfuscationTab::setupEncryptionGroup() +{ encryptionGroup = new QGroupBox("Encrypted Syscall Numbers"); QFormLayout* encryptionLayout = new QFormLayout(); + indirectEncryptSyscalls = new QCheckBox("Encrypt Syscall Numbers"); indirectEncryptSyscalls->setChecked(settings->value("obfuscation/indirect_encrypt_syscalls", false).toBool()); indirectEncryptSyscalls->setToolTip("Encrypt the syscall numbers in the generated shellcode"); @@ -74,30 +107,38 @@ void IndirectObfuscationTab::setupEncryptionGroup() { encryptionGroup->setLayout(encryptionLayout); } -void IndirectObfuscationTab::setupControlFlowGroup() { +void IndirectObfuscationTab::setupControlFlowGroup() +{ controlFlowGroup = new QGroupBox("Control Flow Obfuscation"); QFormLayout* controlFlowLayout = new QFormLayout(); + indirectEnableControlFlow = new QCheckBox("Enable Control Flow Obfuscation"); indirectEnableControlFlow->setChecked(settings->value("obfuscation/indirect_enable_control_flow", false).toBool()); indirectEnableControlFlow->setToolTip("Enable control flow obfuscation for indirect stubs"); controlFlowLayout->addRow(indirectEnableControlFlow); + indirectControlFlowMethod = new QComboBox(); indirectControlFlowMethod->addItem("Random Pattern", "random"); indirectControlFlowMethod->addItem("Register Based (Safest)", "register"); indirectControlFlowMethod->addItem("Value Based", "value"); indirectControlFlowMethod->addItem("Flag Based", "flag"); indirectControlFlowMethod->addItem("Mixed Junk Code", "mixed"); + QString savedMethod = settings->value("obfuscation/indirect_control_flow_method", "random").toString(); int index = indirectControlFlowMethod->findData(savedMethod); - if (index >= 0) { + + if (index >= 0) + { indirectControlFlowMethod->setCurrentIndex(index); } + indirectControlFlowMethod->setToolTip("Choose the control flow obfuscation method"); controlFlowLayout->addRow("Control Flow Method:", indirectControlFlowMethod); controlFlowGroup->setLayout(controlFlowLayout); } -void IndirectObfuscationTab::saveSettings() { +void IndirectObfuscationTab::saveSettings() +{ settings->setValue("obfuscation/indirect_min_instructions", indirectMinInstructions->value()); settings->setValue("obfuscation/indirect_max_instructions", indirectMaxInstructions->value()); settings->setValue("obfuscation/indirect_enable_junk", true); @@ -109,5 +150,6 @@ void IndirectObfuscationTab::saveSettings() { settings->setValue("obfuscation/indirect_control_flow_method", indirectControlFlowMethod->currentData().toString()); } -void IndirectObfuscationTab::loadSettings() { -} +void IndirectObfuscationTab::loadSettings() +{ +} diff --git a/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp b/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp index 571c886..00d1f14 100644 --- a/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp @@ -2,19 +2,24 @@ #include #include -InlineObfuscationTab::InlineObfuscationTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +InlineObfuscationTab::InlineObfuscationTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void InlineObfuscationTab::initUI() { +void InlineObfuscationTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); - + QLabel* comingSoonLabel = new QLabel("Inline Assembly Obfuscation is coming soon!"); comingSoonLabel->setAlignment(Qt::AlignCenter); comingSoonLabel->setStyleSheet("font-size: 16px; color: #888888; margin: 50px;"); - + layout->addWidget(comingSoonLabel); } -void InlineObfuscationTab::saveSettings() {} +void InlineObfuscationTab::saveSettings() +{ +} diff --git a/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp b/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp index 624fd4b..fabcf06 100644 --- a/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp @@ -7,84 +7,119 @@ #include #include -IntegrityTab::IntegrityTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +IntegrityTab::IntegrityTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void IntegrityTab::initUI() { +void IntegrityTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); + QGroupBox* syscallGroup = new QGroupBox("Syscall Selection"); syscallGroup->setToolTip("Select which syscalls to include in the final build"); + QVBoxLayout* syscallLayout = new QVBoxLayout(); + QLabel* desc = new QLabel("Select the syscalls to include in the final build. Only selected syscalls will be processed during integrity checks and included in the final build!"); desc->setWordWrap(true); syscallLayout->addWidget(desc); + QHBoxLayout* selectLayout = new QHBoxLayout(); + QPushButton* selectAllBtn = new QPushButton("Select All"); connect(selectAllBtn, &QPushButton::clicked, this, &IntegrityTab::selectAllSyscalls); selectLayout->addWidget(selectAllBtn); + QPushButton* selectNoneBtn = new QPushButton("Select None"); connect(selectNoneBtn, &QPushButton::clicked, this, &IntegrityTab::selectNoSyscalls); selectLayout->addWidget(selectNoneBtn); + filterEdit = new QLineEdit(); filterEdit->setPlaceholderText("Filter syscalls..."); connect(filterEdit, &QLineEdit::textChanged, this, &IntegrityTab::filterSyscalls); selectLayout->addWidget(filterEdit, 1); syscallLayout->addLayout(selectLayout); + syscallList = new QListWidget(); syscallList->setSelectionMode(QAbstractItemView::NoSelection); syscallLayout->addWidget(syscallList); syscallGroup->setLayout(syscallLayout); layout->addWidget(syscallGroup); + loadSyscalls(); } -void IntegrityTab::loadSyscalls() { +void IntegrityTab::loadSyscalls() +{ syscalls.clear(); PathUtils::debugPathDetection(); + QString projectRoot = PathUtils::getProjectRoot(); qDebug() << "Project root:" << projectRoot; + QString syscallMode = settings->value("general/syscall_mode", "Nt").toString(); bool isKernelMode = (syscallMode == "Zw"); QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK"; QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); + qDebug() << "Header path:" << headerPath; qDebug() << "Header file exists:" << QFile::exists(headerPath); + QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList(); + QFile file(headerPath); - if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) { + + if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) + { qDebug() << "Successfully opened header file:" << headerPath; + QTextStream stream(&file); stream.setCodec("UTF-8"); int lineCount = 0; - while (!stream.atEnd()) { + + while (!stream.atEnd()) + { QString line = stream.readLine(); lineCount++; + QRegExp externCRegex(QString(R"(extern "C" (?:NTSTATUS|ULONG|BOOLEAN|VOID) (%1\w+)\()").arg(syscallPrefix)); - if (externCRegex.indexIn(line) != -1) { + + if (externCRegex.indexIn(line) != -1) + { QString syscall = externCRegex.cap(1); qDebug() << "Found extern C syscall:" << syscall; syscalls.append(syscall); continue; } + QRegExp regularRegex(QString(R"((?:NTSTATUS|ULONG|BOOLEAN|VOID) (%1\w+)\()").arg(syscallPrefix)); - if (regularRegex.indexIn(line) != -1) { + + if (regularRegex.indexIn(line) != -1) + { QString syscall = regularRegex.cap(1); qDebug() << "Found regular syscall:" << syscall; syscalls.append(syscall); continue; } + QRegExp scExternCRegex(R"(extern "C" (?:NTSTATUS|ULONG|BOOLEAN|VOID) (SC\w+)\()"); - if (scExternCRegex.indexIn(line) != -1) { + + if (scExternCRegex.indexIn(line) != -1) + { QString scName = scExternCRegex.cap(1); QString syscallName = syscallPrefix + scName.mid(2); qDebug() << "Found SC extern C syscall:" << scName << "->" << syscallName; syscalls.append(syscallName); continue; } + QRegExp scRegularRegex(R"((?:NTSTATUS|ULONG|BOOLEAN|VOID) (SC\w+)\()"); - if (scRegularRegex.indexIn(line) != -1) { + + if (scRegularRegex.indexIn(line) != -1) + { QString scName = scRegularRegex.cap(1); QString syscallName = syscallPrefix + scName.mid(2); qDebug() << "Found SC regular syscall:" << scName << "->" << syscallName; @@ -92,69 +127,104 @@ void IntegrityTab::loadSyscalls() { continue; } } + qDebug() << "Processed" << lineCount << "lines from header file"; file.close(); - } else { + } + else + { qWarning() << "Failed to open header file:" << headerPath; qWarning() << "File exists:" << file.exists(); qWarning() << "File error:" << file.errorString(); } + syscalls.sort(); qDebug() << "Found" << syscalls.size() << "syscalls"; - for (const QString& syscall : syscalls) { + + for (const QString& syscall : syscalls) + { qDebug() << " " << syscall; } - if (selectedSyscalls.isEmpty()) { + + if (selectedSyscalls.isEmpty()) + { selectedSyscalls = syscalls; } - for (const QString& syscall : syscalls) { + + for (const QString& syscall : syscalls) + { QListWidgetItem* item = new QListWidgetItem(syscall); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - if (selectedSyscalls.contains(syscall)) { + + if (selectedSyscalls.contains(syscall)) + { item->setCheckState(Qt::Checked); - } else { + } + else + { item->setCheckState(Qt::Unchecked); } + syscallList->addItem(item); } } -void IntegrityTab::selectAllSyscalls() { - for (int i = 0; i < syscallList->count(); ++i) { +void IntegrityTab::selectAllSyscalls() +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (!item->isHidden()) { + + if (!item->isHidden()) + { item->setCheckState(Qt::Checked); } } } -void IntegrityTab::selectNoSyscalls() { - for (int i = 0; i < syscallList->count(); ++i) { +void IntegrityTab::selectNoSyscalls() +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (!item->isHidden()) { + + if (!item->isHidden()) + { item->setCheckState(Qt::Unchecked); } } } -void IntegrityTab::filterSyscalls(const QString& text) { - for (int i = 0; i < syscallList->count(); ++i) { +void IntegrityTab::filterSyscalls(const QString& text) +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (text.isEmpty() || item->text().toLower().contains(text.toLower())) { + + if (text.isEmpty() || item->text().toLower().contains(text.toLower())) + { item->setHidden(false); - } else { + } + else + { item->setHidden(true); } } } -void IntegrityTab::saveSettings() { +void IntegrityTab::saveSettings() +{ QStringList selectedSyscalls; - for (int i = 0; i < syscallList->count(); ++i) { + + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (item->checkState() == Qt::Checked) { + + if (item->checkState() == Qt::Checked) + { selectedSyscalls.append(item->text()); } } + settings->setValue("integrity/selected_syscalls", selectedSyscalls); -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp b/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp index f94ab70..b34f2ba 100644 --- a/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp @@ -1,44 +1,57 @@ #include "include/GUI/Settings/Tabs/ObfuscationTab.h" +#include "include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h" #include -ObfuscationTab::ObfuscationTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +ObfuscationTab::ObfuscationTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void ObfuscationTab::initUI() { +void ObfuscationTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); + QGroupBox* junkGroup = new QGroupBox("Junk Instructions"); QFormLayout* junkLayout = new QFormLayout(); + minInstructions = new QSpinBox(); minInstructions->setRange(1, 10); minInstructions->setValue(settings->value("obfuscation/min_instructions", 2).toInt()); junkLayout->addRow("Minimum Instructions:", minInstructions); + maxInstructions = new QSpinBox(); maxInstructions->setRange(1, 20); maxInstructions->setValue(settings->value("obfuscation/max_instructions", 8).toInt()); junkLayout->addRow("Maximum Instructions:", maxInstructions); + useAdvancedJunk = new QCheckBox("Advanced Junk Instructions"); useAdvancedJunk->setChecked(settings->value("obfuscation/use_advanced_junk", false).toBool()); junkLayout->addRow(useAdvancedJunk); junkGroup->setLayout(junkLayout); layout->addWidget(junkGroup); + QGroupBox* nameGroup = new QGroupBox("Name Randomization"); QFormLayout* nameLayout = new QFormLayout(); + QHBoxLayout* syscallPrefixLayout = new QHBoxLayout(); syscallPrefixLength = new QSpinBox(); syscallPrefixLength->setRange(4, 16); syscallPrefixLength->setValue(settings->value("obfuscation/syscall_prefix_length", 8).toInt()); syscallPrefixLength->setToolTip("Length of the syscall prefix in the syscall stub"); + syscallNumberLength = new QSpinBox(); syscallNumberLength->setRange(4, 16); syscallNumberLength->setValue(settings->value("obfuscation/syscall_number_length", 6).toInt()); syscallNumberLength->setToolTip("Length of the syscall number in the syscall stub"); + syscallPrefixLayout->addWidget(new QLabel("Chars:")); syscallPrefixLayout->addWidget(syscallPrefixLength); syscallPrefixLayout->addWidget(new QLabel("Numbers:")); syscallPrefixLayout->addWidget(syscallNumberLength); nameLayout->addRow("Syscall Name Length:", syscallPrefixLayout); + offsetNameLength = new QSpinBox(); offsetNameLength->setRange(4, 16); offsetNameLength->setValue(settings->value("obfuscation/offset_name_length", 8).toInt()); @@ -46,83 +59,105 @@ void ObfuscationTab::initUI() { nameLayout->addRow("Offset Name Length:", offsetNameLength); nameGroup->setLayout(nameLayout); layout->addWidget(nameGroup); + QGroupBox* sequenceGroup = new QGroupBox("Sequence Shuffling"); QFormLayout* sequenceLayout = new QFormLayout(); + shuffleSequence = new QCheckBox("Enable Sequence Shuffling"); shuffleSequence->setChecked(settings->value("obfuscation/shuffle_sequence", true).toBool()); shuffleSequence->setToolTip("Randomize the order of syscall stubs in the assembly file"); sequenceLayout->addRow(shuffleSequence); sequenceGroup->setLayout(sequenceLayout); layout->addWidget(sequenceGroup); + QGroupBox* encryptionGroup = new QGroupBox("Syscall Encryption"); QFormLayout* encryptionLayout = new QFormLayout(); + enableEncryption = new QCheckBox("Enable Syscall ID Encryption"); enableEncryption->setChecked(settings->value("obfuscation/enable_encryption", true).toBool()); enableEncryption->setToolTip("Encrypt syscall IDs in the data section to make static analysis harder"); encryptionLayout->addRow(enableEncryption); + encryptionMethod = new QComboBox(); - encryptionMethod->addItem("Basic XOR (Simple)", 1); - encryptionMethod->addItem("Multi-key XOR (Medium)", 2); - encryptionMethod->addItem("Add + XOR (Medium)", 3); - encryptionMethod->addItem("Enhanced XOR (Medium)", 4); - encryptionMethod->addItem("Offset Shifting (Medium)", 5); - int currentMethod = settings->value("obfuscation/encryption_method", 1).toInt(); + encryptionMethod->addItem("Basic XOR (Simple)", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)); + encryptionMethod->addItem("Multi key XOR (Medium)", static_cast(DirectObfuscation::EncryptionMethod::MultiKeyXOR)); + encryptionMethod->addItem("Add + XOR (Medium)", static_cast(DirectObfuscation::EncryptionMethod::AddXORCombo)); + encryptionMethod->addItem("Enhanced XOR (Medium)", static_cast(DirectObfuscation::EncryptionMethod::EnhancedXOR)); + encryptionMethod->addItem("Offset Shifting (Medium)", static_cast(DirectObfuscation::EncryptionMethod::OffsetShifting)); + + int currentMethod = settings->value("obfuscation/encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt(); int index = encryptionMethod->findData(currentMethod); - if (index >= 0) { + + if (index >= 0) + { encryptionMethod->setCurrentIndex(index); } + encryptionMethod->setToolTip("Select the encryption method to use for syscall ID obfuscation"); encryptionLayout->addRow("Encryption Method:", encryptionMethod); encryptionGroup->setLayout(encryptionLayout); layout->addWidget(encryptionGroup); + QGroupBox* chunkingGroup = new QGroupBox("Function Chunking"); QFormLayout* chunkingLayout = new QFormLayout(); + enableChunking = new QCheckBox("Enable Function Chunking"); enableChunking->setChecked(settings->value("obfuscation/enable_chunking", true).toBool()); enableChunking->setToolTip("Split syscall stubs into multiple fragments to make analysis harder"); chunkingLayout->addRow(enableChunking); chunkingGroup->setLayout(chunkingLayout); layout->addWidget(chunkingGroup); + QGroupBox* interleavedGroup = new QGroupBox("Interleaved Execution"); QFormLayout* interleavedLayout = new QFormLayout(); + enableInterleaved = new QCheckBox("Enable Interleaved Execution"); enableInterleaved->setChecked(settings->value("obfuscation/enable_interleaved", true).toBool()); enableInterleaved->setToolTip("Mix code from different syscalls using ALIGN directives and random padding"); interleavedLayout->addRow(enableInterleaved); interleavedGroup->setLayout(interleavedLayout); layout->addWidget(interleavedGroup); + QGroupBox* controlFlowGroup = new QGroupBox("Control Flow"); QFormLayout* controlFlowLayout = new QFormLayout(); + enableControlFlow = new QCheckBox("Enable Control Flow"); enableControlFlow->setChecked(settings->value("obfuscation/control_flow_enabled", false).toBool()); enableControlFlow->setToolTip("Enable control flow obfuscation techniques"); - controlFlowLayout->addRow(enableControlFlow); + controlFlowLayout->addRow(enableControlFlow); + opaquePredicates = new QCheckBox("Opaque Predicates"); opaquePredicates->setChecked(settings->value("obfuscation/control_flow_opaque_predicates", false).toBool()); opaquePredicates->setToolTip("Add conditional statements that always evaluate to true/false"); - controlFlowLayout->addRow(opaquePredicates); + controlFlowLayout->addRow(opaquePredicates); + bogusControlFlow = new QCheckBox("Bogus Control Flow"); bogusControlFlow->setChecked(settings->value("obfuscation/control_flow_bogus_flow", false).toBool()); bogusControlFlow->setToolTip("Add fake conditional branches that never execute"); - controlFlowLayout->addRow(bogusControlFlow); + controlFlowLayout->addRow(bogusControlFlow); + indirectJumps = new QCheckBox("Indirect Jumps"); indirectJumps->setChecked(settings->value("obfuscation/control_flow_indirect_jumps", false).toBool()); indirectJumps->setToolTip("Use indirect addressing for jump instructions"); - controlFlowLayout->addRow(indirectJumps); + controlFlowLayout->addRow(indirectJumps); + conditionalBranches = new QCheckBox("Conditional Branches"); conditionalBranches->setChecked(settings->value("obfuscation/control_flow_conditional_branches", false).toBool()); conditionalBranches->setToolTip("Add conditional branches"); - controlFlowLayout->addRow(conditionalBranches); + controlFlowLayout->addRow(conditionalBranches); + controlFlowComplexity = new QSpinBox(); controlFlowComplexity->setRange(1, 10); controlFlowComplexity->setValue(settings->value("obfuscation/control_flow_complexity", 2).toInt()); controlFlowComplexity->setToolTip("Number of control flow elements to add"); - controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + controlFlowGroup->setLayout(controlFlowLayout); layout->addWidget(controlFlowGroup); } -void ObfuscationTab::saveSettings() { +void ObfuscationTab::saveSettings() +{ settings->setValue("obfuscation/min_instructions", minInstructions->value()); settings->setValue("obfuscation/max_instructions", maxInstructions->value()); settings->setValue("obfuscation/use_advanced_junk", useAdvancedJunk->isChecked()); @@ -140,4 +175,4 @@ void ObfuscationTab::saveSettings() { settings->setValue("obfuscation/control_flow_indirect_jumps", indirectJumps->isChecked()); settings->setValue("obfuscation/control_flow_conditional_branches", conditionalBranches->isChecked()); settings->setValue("obfuscation/control_flow_complexity", controlFlowComplexity->value()); -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp b/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp index 7b3e875..2f832e8 100644 --- a/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp @@ -3,14 +3,18 @@ #include #include -ProfileTab::ProfileTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +ProfileTab::ProfileTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void ProfileTab::initUI() { +void ProfileTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); layout->addSpacing(20); + QPushButton* exportBtn = new QPushButton("Export Profile (.ini)"); exportBtn->setMinimumHeight(40); exportBtn->setStyleSheet( @@ -27,9 +31,12 @@ void ProfileTab::initUI() { " border: 1px solid rgba(255, 255, 255, 0.3);" "}" ); + connect(exportBtn, &QPushButton::clicked, this, &ProfileTab::exportProfile); layout->addWidget(exportBtn); + layout->addSpacing(10); + QPushButton* importBtn = new QPushButton("Import Profile (.ini)"); importBtn->setMinimumHeight(40); importBtn->setStyleSheet( @@ -46,61 +53,92 @@ void ProfileTab::initUI() { " border: 1px solid rgba(255, 255, 255, 0.3);" "}" ); + connect(importBtn, &QPushButton::clicked, this, &ProfileTab::importProfile); layout->addWidget(importBtn); + layout->addStretch(); } -void ProfileTab::exportProfile() { +void ProfileTab::exportProfile() +{ QString path = QFileDialog::getSaveFileName(this, "Export Profile", "", "INI Files (*.ini);;All Files (*)"); - if (path.isEmpty()) { + + if (path.isEmpty()) + { return; } - if (!path.toLower().endsWith(".ini")) { + + if (!path.toLower().endsWith(".ini")) + { path += ".ini"; } - try { + + try + { QSettings exportSettings(path, QSettings::IniFormat); exportSettings.clear(); + settings->sync(); - for (const QString& group : settings->childGroups()) { + + for (const QString& group : settings->childGroups()) + { settings->beginGroup(group); exportSettings.beginGroup(group); - for (const QString& key : settings->childKeys()) { + + for (const QString& key : settings->childKeys()) + { exportSettings.setValue(key, settings->value(key)); } + exportSettings.endGroup(); settings->endGroup(); } + exportSettings.sync(); - QMessageBox::information(this, "Bind - v1.3.0", - QString("Profile exported to:\n%1").arg(QDir::toNativeSeparators(QFileInfo(path).absoluteFilePath()))); - } catch (...) { - QMessageBox::critical(this, "Bind - v1.3.0", "Failed to export profile."); + + QMessageBox::information(this, "Bind - v1.3.1", + QString("Profile exported to:\n%1") + .arg(QDir::toNativeSeparators(QFileInfo(path).absoluteFilePath()))); + } + catch (...) + { + QMessageBox::critical(this, "Bind - v1.3.1", "Failed to export profile."); } } -void ProfileTab::importProfile() { - QString path = QFileDialog::getOpenFileName(this, "Bind - v1.3.0", "", "INI Files (*.ini);;All Files (*)"); - if (path.isEmpty()) { +void ProfileTab::importProfile() +{ + QString path = QFileDialog::getOpenFileName(this, "Bind - v1.3.1", "", "INI Files (*.ini);;All Files (*)"); + + if (path.isEmpty()) + { return; } - try { + + try + { QString iniPath = PathUtils::getIniPath(); settings->sync(); delete settings; settings = nullptr; + QFile::remove(iniPath); QFile::copy(path, iniPath); - QMessageBox::information(this, "Bind - v1.3.0", + + QMessageBox::information(this, "Bind - v1.3.1", QString("Profile imported from:\n%1\n\nSysCaller will now restart to use the imported profile.") .arg(QDir::toNativeSeparators(QFileInfo(path).absoluteFilePath()))); + QProcess::startDetached(QApplication::applicationFilePath(), QApplication::arguments()); QApplication::quit(); - } catch (...) { - QMessageBox::critical(this, "Bind - v1.3.0", "Failed to import profile."); + } + catch (...) + { + QMessageBox::critical(this, "Bind - v1.3.1", "Failed to import profile."); } } -void ProfileTab::saveSettings() { -} \ No newline at end of file +void ProfileTab::saveSettings() +{ +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/CompatibilityThread.cpp b/Bind/src/GUI/Threads/CompatibilityThread.cpp index 45becf2..eaaf17d 100644 --- a/Bind/src/GUI/Threads/CompatibilityThread.cpp +++ b/Bind/src/GUI/Threads/CompatibilityThread.cpp @@ -3,40 +3,58 @@ #include #include -CompatibilityThread::CompatibilityThread(QObject* parent) : QThread(parent) { -} +CompatibilityThread::CompatibilityThread(QObject* parent) + : QThread(parent) +{} -void CompatibilityThread::setDllPaths(const QStringList& paths) { +void CompatibilityThread::setDllPaths(const QStringList& paths) +{ dllPaths = paths; } -void CompatibilityThread::run() { +void CompatibilityThread::run() +{ qDebug() << "CompatibilityThread::run() called"; qDebug() << "DLL paths in thread:" << dllPaths; + emit compatibilityStarted(); emit progressUpdated("Starting Compatibility Check..."); - try { + + try + { qDebug() << "Creating Compatibility instance..."; + Compatibility compatibility; - compatibility.setOutputCallback([this](const QString& message) { + compatibility.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); + emit progressUpdated("Processing NTDLL..."); qDebug() << "Calling compatibility.runWithDllPaths()..."; + int result = compatibility.runWithDllPaths(dllPaths); - if (result == 0) { + + if (result == 0) + { emit progressUpdated("Compatibility Check Completed Successfully"); emit compatibilityFinished(true, "Compatibility Check Completed Successfully!"); - } else { + } + else + { emit progressUpdated("Compatibility Check Failed"); emit compatibilityFinished(false, "Compatibility Check failed with Error Code: " + QString::number(result)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { QString errorMsg = QString("Compatibility Error: %1").arg(e.what()); emit progressUpdated("Compatibility Error Occurred"); emit compatibilityFinished(false, errorMsg); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown Compatibility Error Occurred"); emit compatibilityFinished(false, "Unknown Error occurred during Compatibility Check"); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/ObfuscationThread.cpp b/Bind/src/GUI/Threads/ObfuscationThread.cpp index 4dd6d74..1b2b0b2 100644 --- a/Bind/src/GUI/Threads/ObfuscationThread.cpp +++ b/Bind/src/GUI/Threads/ObfuscationThread.cpp @@ -7,64 +7,96 @@ #include #include -ObfuscationThread::ObfuscationThread(QObject* parent) : QThread(parent) { -} +ObfuscationThread::ObfuscationThread(QObject* parent) + : QThread(parent) +{} -void ObfuscationThread::setOutputCallback(std::function callback) { +void ObfuscationThread::setOutputCallback(std::function callback) +{ outputCallback = callback; } -void ObfuscationThread::run() { +void ObfuscationThread::run() +{ qDebug() << "ObfuscationThread::run() called"; emit obfuscationStarted(); emit progressUpdated("Starting Syscall Obfuscation..."); - try { + + try + { qDebug() << "Creating Obfuscation Instance..."; + Obfuscation obfuscation; - obfuscation.setOutputCallback([this](const QString& message) { + obfuscation.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); + emit progressUpdated("Processing Syscall List For Obfuscation..."); qDebug() << "Calling obfuscation.run()..."; + int result = obfuscation.run(); - if (result == 0) { + + if (result == 0) + { emit progressUpdated("Stubs & Exports Generated Successfully!"); + QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); bool hashStubsEnabled = settings.value("general/hash_stubs", false).toBool(); - if (hashStubsEnabled) { + + if (hashStubsEnabled) + { QString lastMethod = settings.value("obfuscation/last_method", "").toString(); QString obfuscationType = (lastMethod == "stub_mapper") ? "Stub Mapper" : "Normal"; + emit progressUpdated(QString("Generating Stub Hashes for %1 Obfuscation...").arg(obfuscationType)); - try { + + try + { bool isKernelMode = settings.value("general/syscall_mode", "Nt").toString() == "Zw"; QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); QVariantMap stubHashes = StubHashGenerator::generateStubHashes(asmPath, headerPath, lastMethod); QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"); QPair saveResult = StubHashGenerator::saveStubHashes(stubHashes, timestamp); - if (saveResult.first) { + + if (saveResult.first) + { QString fileName = QFileInfo(saveResult.second).fileName(); emit progressUpdated(QString("Stub Hashes Saved to: %1").arg(fileName)); - } else { + } + else + { emit progressUpdated(QString("Failed to save Stub Hashes: %1").arg(saveResult.second)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { emit progressUpdated(QString("Error Generating Stub Hashes: %1").arg(e.what())); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown error occurred while generating Stub Hashes"); } } + emit obfuscationFinished(true, "Obfuscation Completed Successfully!"); - } else { + } + else + { emit progressUpdated("Obfuscation Failed With Error Code: " + QString::number(result)); emit obfuscationFinished(false, "Obfuscation failed with Error Code: " + QString::number(result)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { QString errorMsg = QString("Obfuscation Error: %1").arg(e.what()); emit progressUpdated("Obfuscation Error Occurred"); emit obfuscationFinished(false, errorMsg); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown Obfuscation Error Occurred"); emit obfuscationFinished(false, "Unknown Error occurred during Obfuscation"); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/ValidatorThread.cpp b/Bind/src/GUI/Threads/ValidatorThread.cpp index 27e5825..68b425e 100644 --- a/Bind/src/GUI/Threads/ValidatorThread.cpp +++ b/Bind/src/GUI/Threads/ValidatorThread.cpp @@ -3,42 +3,58 @@ #include #include -ValidatorThread::ValidatorThread(QObject* parent) : QThread(parent) { -} +ValidatorThread::ValidatorThread(QObject* parent) + : QThread(parent) +{} -void ValidatorThread::setDllPaths(const QStringList& paths) { +void ValidatorThread::setDllPaths(const QStringList& paths) +{ dllPaths = paths; } -void ValidatorThread::run() { +void ValidatorThread::run() +{ qDebug() << "ValidatorThread::run() called"; qDebug() << "DLL paths in thread:" << dllPaths; + emit validationStarted(); emit progressUpdated("Starting Validation Check..."); - try { + + try + { qDebug() << "Creating Validator Instance..."; + Validator validator; - validator.setOutputCallback([this](const QString& message) { + validator.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); + emit progressUpdated("Processing NTDLL..."); qDebug() << "Calling validator.runWithDllPaths()..."; + int result = validator.runWithDllPaths(dllPaths); - if (result == 0) { + + if (result == 0) + { emit progressUpdated("Validation Check Completed Successfully"); emit validationFinished(true, "Validation Completed Successfully!"); - } else { + } + else + { emit progressUpdated("Validation Check Failed"); emit validationFinished(false, "Validation failed with Error Code: " + QString::number(result)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { QString errorMsg = QString("Validation Error: %1").arg(e.what()); emit progressUpdated("Validation Error Occurred"); emit validationFinished(false, errorMsg); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown Validation Error Occurred"); emit validationFinished(false, "Unknown Error occurred during Validation"); } -} - - \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/VerificationThread.cpp b/Bind/src/GUI/Threads/VerificationThread.cpp index 9b8ca62..27cd127 100644 --- a/Bind/src/GUI/Threads/VerificationThread.cpp +++ b/Bind/src/GUI/Threads/VerificationThread.cpp @@ -1,26 +1,35 @@ #include "include/GUI/Threads/VerificationThread.h" #include -VerificationThread::VerificationThread(QObject* parent) : QThread(parent) { -} +VerificationThread::VerificationThread(QObject* parent) + : QThread(parent) +{} -void VerificationThread::setDllPaths(const QStringList& paths) { +void VerificationThread::setDllPaths(const QStringList& paths) +{ dllPaths = paths; } -void VerificationThread::setOutputCallback(std::function callback) { +void VerificationThread::setOutputCallback(std::function callback) +{ outputCallback = callback; - verification.setOutputCallback([this](const QString& message) { + verification.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); } -void VerificationThread::run() { +void VerificationThread::run() +{ emit verificationStarted(); - try { + + try + { int result = verification.runWithDllPaths(dllPaths); emit verificationFinished(result == 0, result == 0 ? "Verification Completed Successfully" : "Verification Failed"); - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { qDebug() << "Verification Thread Exception:" << e.what(); emit verificationFinished(false, QString("Verification Failed: %1").arg(e.what())); } diff --git a/Bind/src/Res/Icons/compatibility.png b/Bind/src/Res/Icons/compatibility.png new file mode 100644 index 0000000000000000000000000000000000000000..16d2ef5891b4d2ab8f4614e0bdcf977aac286847 GIT binary patch literal 921 zcmV;K17`e*P)rNnZ@F_+X6sB0^Nk3yCE$F~kIT<1b)CHgqR8 zwGU8eOSg3v)@9kQ)7hQBdp&o$t#|Ux+;i_aXU_NCbER!xPeM@?v?P;|B2WQ-mErNUQctAIqswpn)vCH zy~hu}+OswN#)j87Ps~lGC~kB$fy@~@?|EL{wygYLH!BNGLFzc{ANZ2jOAncsZ$&WG9G?p@Ys ze#)o7XvR4dC;fhZZfqKl@{fvBe@qmE(P%o>mBf{cBj$^)OWcV(*_CSRig(01+RIC2 zqhU4nP=a9!&l0YwMB$|1&pZz@Q8D{A;Qp@{kjXxdcE1+sNk3=G1 zjNxkTnqR4EbX=xN7;HTaX?dge;KElgZy72@Lp_gCsWgQmk*)f2b$skbLD;WsAIQMv z{+@&%65_pLR5yXQ&*t~-9Lm;ewKYA$;V}Np-I-c8YG=t5It(Ki&=6EdR5FH$(-*># zCuY6_2}!2{+p%GqCd|5tP%s1vw#a3qne!w$KtRF%(3$;%2M#7$+FpA4RNs+oT^Dzl z+g^H6f@zynDfNfKVHrk+xvtkta2kr5Mj)U`_%@y&-1Tl&{7ndCS|;WybC4w&s-pVd zoF3PxSC};x3EH2Rcm#pFKNo-k{u3VX)c5C)I!IxQ{ z=RB)#4h^)X5Tq4?wP-%$Hv3t{Ek|68mCMm913ePl2=SKuW73+OHP9Af|92`H)QS&X zTE1 \ No newline at end of file diff --git a/Bind/src/Res/Icons/export.png b/Bind/src/Res/Icons/export.png new file mode 100644 index 0000000000000000000000000000000000000000..f78bfb28860d7002c7f934eea7026f84ddaa9c40 GIT binary patch literal 628 zcmV-)0*n2LP)Q2n3Lvhd%vJrBZ>iZNu65In>cVDV5;M z#>T5uKEH@;ap^_?BmyWQLf?qT!R+scheJc4D+M9~{(W0|W4Mc^Rs%Ybz-~w{T)5(BSBLXUH66!bhHo%8-#RwG8d~>1mKs z!qUo$-==xFxIh5 \ No newline at end of file diff --git a/Bind/src/Res/Icons/obfuscation.png b/Bind/src/Res/Icons/obfuscation.png new file mode 100644 index 0000000000000000000000000000000000000000..09f73c230c370b2743eb85fb6fe37959003549ff GIT binary patch literal 614 zcmV-s0-61ZP)uo$9za0n!RK6IEMzcnv zalTwGU*lN5Xl%0x@a7@J@ArGLm-qX!KR`P9K+a07Q|7mej3=QxOXz21|}W<$(& zyEQ<76V{nFm&$id(-!G;T1=)7V15*`GD!Mkp^TWPo31V`HKb`*=n zsMTuGa5&V#7ubEitc0iadc7)1lHTw4sn_e#Y&N6ucubSYL}wk_T-5`XVyDxY@Fo(8 zP%@dMQmI6>T8;0D$HrCx&br<1LQxdT=kvs+AZ^ZOar^=-*DTI254|ZA3WRb9uXyO! zEAS5JRxhA|PvAV4%ZW@TBQQXsQmGjGY&I)cXWD4TukZxZR}6(h_Y;XkK-YDWWtpPU zC^wIq&89IE;c)mS1}n5)uODMJrttfA808;ujKWE_+wDfmyw2xyL+SB&i~y_E$`~p< z?Y>&A{(|Zr*X#nPPN$Ro$>DGq0eDqam4pz)8QE+$qaSR8J8u3d`%31Y_X0cvf55_I z2{vYi4)a4E;0D~)kKjEAP{1v7{U6za-+u)d05p2oGJV38LI3~&07*qoM6N<$g3})! AAOHXW literal 0 HcmV?d00001 diff --git a/Bind/src/Res/Icons/obfuscation.svg b/Bind/src/Res/Icons/obfuscation.svg deleted file mode 100644 index 8481bf1..0000000 --- a/Bind/src/Res/Icons/obfuscation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Bind/src/Res/Icons/refresh.png b/Bind/src/Res/Icons/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..6d68b75b8602be2f952fc56925f0c3b382b98aa4 GIT binary patch literal 835 zcmV-J1HAl+P)^v(O5NtVlYM%#Ro%7eBuxI;@>bnnD}I(i4wn}iBU8dG$ABV6f`YrDV9R{ zD239}kL~Wxc!z=#o=n{2WM=NpIdjk4JA^R?|MQ85Z%-B`k_4hCf?zO|6GFTgI*DjO z6eE5h2Jx_Gl+vidVCcrR_rncmkGM^+UKH5Xx+?R*YNxruS}fLM%$iDT4Kqtw?_?x% znKCx0Yg)%9LHI*utkNhlZ|6~mtIlb*8VNC}c?uj7A(mRZxw@mpeo8}fS6iiwVwqa63DX<%O9fl$4o3Q0zA!1-VG)ga zMP=V2i8)m7LxE8(li}HQBU-PW--{>bKr#y4F61`__z4(L%_bqHX>=((9Z%_0A1zoD zK$0XcFZKPWOWl+=FFX_l*1G)82ih5|-*szd!sp z0|5Elg%&xJE8J`adVKz5$cKbaSa;xiB%|?=tIBpJo}+6tpPg3~CAu!4Zg`nj2*gqh zYV1YO`*47#H-zK|I$FU7>%s@k0Wgy7S?#d&) zlOdVa*z7E0A(uq}-=kMaHIwsW%dZoO;Mfm_<2Oe(?(HoxnU2{Hc=kx{dK*e|W*Xbu)-8W \ No newline at end of file diff --git a/Bind/src/Res/Icons/settings.png b/Bind/src/Res/Icons/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..0699492c523ff279a3c1448ba8cf7feb6f68aa74 GIT binary patch literal 810 zcmV+_1J(SAP)Y&FW5Ttq#q#_syLQzKHqohOFgD4clvM+`f=t=|G48^S}WVnjL z_RyXCFm+$v{X1uF<$D)?_Z-gecg{Wcf6kv{jKTjr0=>*;Gw?hQd3kxjaU4)lnM~Gz zR8>~zlO)NV<5H;_{L< zX&j}1i9(7-MIfJI#07)vu(axj#_n#N2+*79pscK{S*=#zA`A{%zj-{%%;9i)42Ig< z1oAVnvEFT?(KsH9#aNR{^=t&-oRIBc3Ja~`312f9>W_p%VbEx_`iY76dg7zeby)lQ z3WTIn?i@R1*6DQpk$8Oc5-wP#0G=wDD^}~Ug~q5<9)PK-H{WY(8x9j8uDy=IwnJ_* zorI@jZ=ra9s5lbgeNKg9_YiSO2(2bl^W*vX6{gV~dstLd^uXm>Vl$J&%z9Vw#{xa< z(Lg;rd!qC?X|p-I1u{F0W0K3|ZH0w}eFX&tN6C6V%(6*)6Qkt*9?o4?z3&NWNV|9c zb9dqpLWeLA@M3jbZk5fl&Cl%qP5#rFI_S!Bm59>ZJ<6;y-ZC1eB8$Bj?B`W8Hy+*s o+lv{tn2kw@EyOk={YQWS00n%6Wy2Jn8UO$Q07*qoM6N<$f>gGAWB>pF literal 0 HcmV?d00001 diff --git a/Bind/src/Res/Icons/settings.svg b/Bind/src/Res/Icons/settings.svg deleted file mode 100644 index 73f58d3..0000000 --- a/Bind/src/Res/Icons/settings.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Bind/src/Res/Icons/validation.png b/Bind/src/Res/Icons/validation.png new file mode 100644 index 0000000000000000000000000000000000000000..210b1a6c3ccefb387a9f902b012f61aa83eb775e GIT binary patch literal 724 zcmV;_0xSKAP)OzWUn z0;LtWZ01tXg<8j;Z_I_HKrTI+R#Ne#91TW8JU`7;ul=m;PH$CyJhs|ao-Hj8WYC4r z1T7}-!!nc)MnE)Beai(YB()laHos380a;w!sv)P1YS!k&(92_2?TrT5v`X%=E_V8$E7MsOQc!no)rgsMpO$rb~ znx$SzTE!+Dt2H= zl6+;~PXSRD8)o5><1;;jHvL`Gj?8DJ7yk74_!t{^`ruUtU*NUq$R?J_nf?quR*&`@ zqAX(S``lm9hd(j+Vh`pr|L6_Q^cyTSck9~%h(SgPu5Ui*zy$`64#J-28dg=`=1kAP zqZL \ No newline at end of file diff --git a/Bind/src/Res/Icons/verification.png b/Bind/src/Res/Icons/verification.png new file mode 100644 index 0000000000000000000000000000000000000000..eb476950f19916dfba9980ee1a071bd486232f4b GIT binary patch literal 773 zcmV+g1N!`lP)QOI*BL_|JqP>f{|vL-P6P^2`%z#CpQr zO_QIezLpylnB{SL4h*(kInCF^FbpITiG7>8uJan-g_Au#hrg>;I4LCoGGtC0qI;mS1?Tzs;YvI97?59h_A-+D*PC0Yik&C z^&_?t!^Y-Dfio3Tu(mW^%aL_gbRdEgFU~;AYhVS2XkSE>kZrWQ?xq_+HB^I`MA?DK zzAH*Hdna@gu~-bV{ZnA}A}q_YP-~%WYD|0(`Up#JaZzI}O z0lz1>juO?hhddH=P0J87Uv7}MKBW_iV)IC5@8Z>cP{1DvK3s&kEZH)RU%JAXN(Vd4 zwB96F66?;O6CDeeitseSjS$wk_}4S5JL{lY&XgCCZ_xr9Q|D7%l9Rbyt)BYuTrY5} z;$KpUv7m0|zWrW=+dftzA&J8z(dh`1nKoMxUUpCRJ3FenYXgpuyH>36lAhK-q}O+o z`6v|WE@rJb;uZv3YGB)+ \ No newline at end of file From 3e99bdd09de42ecca7d78f31ee5658f5babaa847 Mon Sep 17 00:00:00 2001 From: WindowsAPI Date: Thu, 25 Sep 2025 21:30:28 -0700 Subject: [PATCH 5/8] actions test --- Bind/Bind.vcxproj | 16 +- .../Direct/Encryption/DirectEncryptor.h | 30 +- .../ControlFlow/IndirectControlFlow.h | 25 +- .../Indirect/Stub/IndirectStubGenerator.h | 24 + Bind/include/Core/Obfuscation/Obfuscation.h | 20 + Bind/resources.qrc | 16 +- .../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, 5431 insertions(+), 1904 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 80080bb..567cba1 100644 --- a/Bind/Bind.vcxproj +++ b/Bind/Bind.vcxproj @@ -416,15 +416,15 @@
- - + + - - - + + + - - + + @@ -460,4 +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..3d27d7f 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..9abb4f8 100644 --- a/Bind/resources.qrc +++ b/Bind/resources.qrc @@ -1,14 +1,14 @@ - 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 @@ -17,4 +17,4 @@ 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 << "\n"; @@ -508,73 +734,120 @@ void HashCompareDialog::exportAsHtml(const QString& exportPath, const QStringLis stream << ".hash-type { font-weight: bold; color: #0b5394; }\n"; stream << "\n\n\n"; stream << "

Bind - Hash Comparison

\n"; + QString firstTimestamp = fileData.value(selectedFiles.first())["timestamp"].toString(); - if (firstTimestamp.isEmpty()) firstTimestamp = "Unknown"; + + if (firstTimestamp.isEmpty()) + { + firstTimestamp = "Unknown"; + } + stream << "

Generated on: " << firstTimestamp << "

\n"; stream << "

Hash Type: " << hashType << "

\n"; stream << "\n"; - 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 << ""; } + stream << "\n"; + QMap>> hashMapping; - for (const QString& syscall : allSyscalls) { - for (const QString& filePath : selectedFiles) { + + for (const QString& syscall : allSyscalls) + { + for (const QString& filePath : selectedFiles) + { QVariantMap jsonData = fileData[filePath]; QVariantMap stubs = jsonData["stubs"].toMap(); QString hashValue = stubs.value(syscall).toString(); - if (!hashValue.isEmpty()) { + + if (!hashValue.isEmpty()) + { QString extractedHash = extractHash(hashValue, hashType); - if (extractedHash != "N/A") { - if (!hashMapping.contains(extractedHash)) { + + if (extractedHash != "N/A") + { + if (!hashMapping.contains(extractedHash)) + { hashMapping[extractedHash] = QList>(); } + hashMapping[extractedHash].append(qMakePair(syscall, filePath)); } } } } + QStringList sortedSyscalls = allSyscalls.values(); std::sort(sortedSyscalls.begin(), sortedSyscalls.end()); - for (const QString& syscall : sortedSyscalls) { + + for (const QString& syscall : sortedSyscalls) + { stream << ""; - 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; } } + QString duplicateClass = ""; - if (displayValue != "N/A") { - if (hashMapping.contains(displayValue) && hashMapping[displayValue].size() > 1) { + + if (displayValue != "N/A") + { + if (hashMapping.contains(displayValue) && hashMapping[displayValue].size() > 1) + { duplicateClass = " class=\"duplicate\""; } } + stream << "" << displayValue << ""; } + stream << "\n"; } + stream << "
Syscall" << timestamp << "
(" << obfMethod << ")
" << syscall << "
\n\n"; file.close(); } -QString HashCompareDialog::getProjectPaths() { +QString HashCompareDialog::getProjectPaths() +{ return PathUtils::getProjectRoot(); } diff --git a/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp b/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp index e414a34..5c98576 100644 --- a/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp +++ b/Bind/src/GUI/Dialogs/ObfuscationSelectionDialog.cpp @@ -13,7 +13,7 @@ ObfuscationSelectionDialog::ObfuscationSelectionDialog(QWidget* parent) : QDialog(parent) , selection(Cancelled) { - setWindowTitle("Bind - v1.3.0"); + setWindowTitle("Bind - v1.3.1"); setFixedSize(450, 300); setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground); @@ -21,9 +21,12 @@ ObfuscationSelectionDialog::ObfuscationSelectionDialog(QWidget* parent) initUI(); } -void ObfuscationSelectionDialog::setupStylesheet() { +void ObfuscationSelectionDialog::setupStylesheet() +{ QFile stylesheetFile(":/src/GUI/Stylesheets/ObfuscationSelectionDialog.qss"); - if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) { + + if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) + { QTextStream in(&stylesheetFile); QString stylesheet = in.readAll(); setStyleSheet(stylesheet); @@ -31,32 +34,40 @@ void ObfuscationSelectionDialog::setupStylesheet() { } } -void ObfuscationSelectionDialog::initUI() { +void ObfuscationSelectionDialog::initUI() +{ QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(30, 30, 30, 30); mainLayout->setSpacing(20); + titleLabel = new QLabel("Obfuscation Selection"); titleLabel->setObjectName("title"); titleLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(titleLabel); + descriptionLabel = new QLabel("Choose the Obfuscation Method you want to use for Syscall Generation:"); descriptionLabel->setObjectName("description"); descriptionLabel->setAlignment(Qt::AlignCenter); descriptionLabel->setWordWrap(true); mainLayout->addWidget(descriptionLabel); + QVBoxLayout* buttonLayout = new QVBoxLayout(); buttonLayout->setSpacing(15); + normalObfuscationButton = new QPushButton("Normal Obfuscation"); normalObfuscationButton->setToolTip("Runs w/ Obfuscation configured from the Obfuscation Settings for all Syscalls."); connect(normalObfuscationButton, &QPushButton::clicked, this, &ObfuscationSelectionDialog::onNormalObfuscationClicked); buttonLayout->addWidget(normalObfuscationButton); + stubMapperButton = new QPushButton("Stub Mapper"); stubMapperButton->setToolTip("Runs w/ Obfuscation configured from Stub Mapper for specially configured Syscalls."); connect(stubMapperButton, &QPushButton::clicked, this, &ObfuscationSelectionDialog::onStubMapperClicked); buttonLayout->addWidget(stubMapperButton); mainLayout->addLayout(buttonLayout); + QHBoxLayout* cancelLayout = new QHBoxLayout(); cancelLayout->addStretch(); + cancelButton = new QPushButton("Cancel"); cancelButton->setObjectName("cancel"); cancelButton->setFixedWidth(100); @@ -65,17 +76,20 @@ void ObfuscationSelectionDialog::initUI() { mainLayout->addLayout(cancelLayout); } -void ObfuscationSelectionDialog::onNormalObfuscationClicked() { +void ObfuscationSelectionDialog::onNormalObfuscationClicked() +{ selection = NormalObfuscation; accept(); } -void ObfuscationSelectionDialog::onStubMapperClicked() { +void ObfuscationSelectionDialog::onStubMapperClicked() +{ selection = StubMapper; accept(); } -void ObfuscationSelectionDialog::onCancelClicked() { +void ObfuscationSelectionDialog::onCancelClicked() +{ selection = Cancelled; reject(); -} +} diff --git a/Bind/src/GUI/Dialogs/SettingsDialog.cpp b/Bind/src/GUI/Dialogs/SettingsDialog.cpp index 340bf97..d44ff08 100644 --- a/Bind/src/GUI/Dialogs/SettingsDialog.cpp +++ b/Bind/src/GUI/Dialogs/SettingsDialog.cpp @@ -17,100 +17,137 @@ #include #include -SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent) { +SettingsDialog::SettingsDialog(QWidget* parent) + : QDialog(parent) +{ setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); setMinimumSize(500, 600); + titleBar = new SettingsTitleBar(this); QString iniPath = PathUtils::getIniPath(); settings = new QSettings(iniPath, QSettings::IniFormat, this); + setupStylesheet(); initUI(); } -void SettingsDialog::initUI() { +void SettingsDialog::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(titleBar); + tabs = new QTabWidget(); + QScrollArea* generalScrollArea = new QScrollArea(); generalScrollArea->setWidgetResizable(true); generalScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); generalScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + generalTab = new GeneralTab(settings); generalTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); generalScrollArea->setWidget(generalTab); + QScrollArea* integrityScrollArea = new QScrollArea(); integrityScrollArea->setWidgetResizable(true); integrityScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); integrityScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + integrityTab = new IntegrityTab(settings); integrityTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); integrityScrollArea->setWidget(integrityTab); + QScrollArea* profileScrollArea = new QScrollArea(); profileScrollArea->setWidgetResizable(true); profileScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); profileScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + profileTab = new ProfileTab(settings); profileTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); profileScrollArea->setWidget(profileTab); + tabs->addTab(generalScrollArea, "General"); tabs->addTab(integrityScrollArea, "Integrity"); + bool inlineAssemblyEnabled = settings->value("general/inline_assembly", false).toBool(); bool indirectAssemblyEnabled = settings->value("general/indirect_assembly", false).toBool(); - if (inlineAssemblyEnabled) { + + if (inlineAssemblyEnabled) + { QScrollArea* inlineObfuscationScrollArea = new QScrollArea(); inlineObfuscationScrollArea->setWidgetResizable(true); inlineObfuscationScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); inlineObfuscationScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + inlineObfuscationTab = new InlineObfuscationTab(settings); inlineObfuscationTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); inlineObfuscationScrollArea->setWidget(inlineObfuscationTab); + tabs->addTab(inlineObfuscationScrollArea, "Obfuscation"); - } else if (indirectAssemblyEnabled) { + } + else if (indirectAssemblyEnabled) + { QScrollArea* indirectObfuscationScrollArea = new QScrollArea(); indirectObfuscationScrollArea->setWidgetResizable(true); indirectObfuscationScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); indirectObfuscationScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + indirectObfuscationTab = new IndirectObfuscationTab(settings); indirectObfuscationTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); indirectObfuscationScrollArea->setWidget(indirectObfuscationTab); + tabs->addTab(indirectObfuscationScrollArea, "Obfuscation"); - } else { + } + else + { QScrollArea* obfuscationScrollArea = new QScrollArea(); obfuscationScrollArea->setWidgetResizable(true); obfuscationScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); obfuscationScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + obfuscationTab = new ObfuscationTab(settings); obfuscationTab->setStyleSheet("QWidget { background: #1E1E1E; color: white; }"); obfuscationScrollArea->setWidget(obfuscationTab); + tabs->addTab(obfuscationScrollArea, "Obfuscation"); } + tabs->addTab(profileScrollArea, "Profile"); layout->addWidget(tabs); + QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->setSpacing(10); buttonLayout->setContentsMargins(20, 10, 20, 10); - if (!inlineAssemblyEnabled && !indirectAssemblyEnabled) { + + if (!inlineAssemblyEnabled && !indirectAssemblyEnabled) + { QPushButton* stubMapperBtn = new QPushButton("Stub Mapper"); stubMapperBtn->setToolTip("Customize Obfuscation Settings for Individual Syscalls"); connect(stubMapperBtn, &QPushButton::clicked, this, &SettingsDialog::openStubMapper); buttonLayout->addWidget(stubMapperBtn); } + QPushButton* saveBtn = new QPushButton("Save"); connect(saveBtn, &QPushButton::clicked, this, &SettingsDialog::saveSettings); + QPushButton* cancelBtn = new QPushButton("Cancel"); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); + buttonLayout->addStretch(); buttonLayout->addWidget(saveBtn); buttonLayout->addWidget(cancelBtn); + layout->addLayout(buttonLayout); connect(titleBar, &SettingsTitleBar::closeClicked, this, &SettingsDialog::reject); } -void SettingsDialog::setupStylesheet() { +void SettingsDialog::setupStylesheet() +{ QFile stylesheetFile(":/src/GUI/Stylesheets/SettingsDialog.qss"); - if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) { + + if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) + { QTextStream in(&stylesheetFile); QString stylesheet = in.readAll(); setStyleSheet(stylesheet); @@ -118,50 +155,69 @@ void SettingsDialog::setupStylesheet() { } } -void SettingsDialog::saveSettings() { +void SettingsDialog::saveSettings() +{ generalTab->saveSettings(); + bool inlineAssemblyEnabled = settings->value("general/inline_assembly", false).toBool(); bool indirectAssemblyEnabled = settings->value("general/indirect_assembly", false).toBool(); - if (inlineAssemblyEnabled) { - if (inlineObfuscationTab) { + + if (inlineAssemblyEnabled) + { + if (inlineObfuscationTab) + { inlineObfuscationTab->saveSettings(); } - } else if (indirectAssemblyEnabled) { - if (indirectObfuscationTab) { + } + else if (indirectAssemblyEnabled) + { + if (indirectObfuscationTab) + { indirectObfuscationTab->saveSettings(); } - } else { - if (obfuscationTab) { + } + else + { + if (obfuscationTab) + { obfuscationTab->saveSettings(); } } + integrityTab->saveSettings(); profileTab->saveSettings(); accept(); } -void SettingsDialog::openStubMapper() { +void SettingsDialog::openStubMapper() +{ StubMapperDialog dialog(this); dialog.exec(); } -void SettingsDialog::mousePressEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void SettingsDialog::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = true; m_dragPosition = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } -void SettingsDialog::mouseMoveEvent(QMouseEvent* event) { - if (event->buttons() & Qt::LeftButton && m_dragging) { +void SettingsDialog::mouseMoveEvent(QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton && m_dragging) + { move(event->globalPos() - m_dragPosition); event->accept(); } } -void SettingsDialog::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void SettingsDialog::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = false; event->accept(); } diff --git a/Bind/src/GUI/Dialogs/StubMapperDialog.cpp b/Bind/src/GUI/Dialogs/StubMapperDialog.cpp index 321db9d..a351dca 100644 --- a/Bind/src/GUI/Dialogs/StubMapperDialog.cpp +++ b/Bind/src/GUI/Dialogs/StubMapperDialog.cpp @@ -1,6 +1,7 @@ #include "include/GUI/Dialogs/StubMapperDialog.h" #include "include/Core/Utils/PathUtils.h" #include "include/GUI/Bars/SettingsTitleBar.h" +#include "include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h" #include #include #include @@ -21,129 +22,173 @@ StubMapperDialog::StubMapperDialog(QWidget* parent) initUI(); } -StubMapperDialog::~StubMapperDialog() { +StubMapperDialog::~StubMapperDialog() +{ delete settings; } -void StubMapperDialog::initUI() { +void StubMapperDialog::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(titleBar); + QSplitter* splitter = new QSplitter(Qt::Horizontal); + QWidget* leftPanel = new QWidget(); QVBoxLayout* leftLayout = new QVBoxLayout(leftPanel); + QGroupBox* syscallGroup = new QGroupBox("Available Syscalls"); QVBoxLayout* syscallLayout = new QVBoxLayout(); QHBoxLayout* filterLayout = new QHBoxLayout(); + QLabel* filterLabel = new QLabel("Filter:"); filterInput = new QLineEdit(); filterInput->setPlaceholderText("Filter Syscalls..."); + connect(filterInput, &QLineEdit::textChanged, this, &StubMapperDialog::filterSyscalls); filterLayout->addWidget(filterLabel); filterLayout->addWidget(filterInput); syscallLayout->addLayout(filterLayout); + syscallList = new QListWidget(); connect(syscallList, &QListWidget::currentItemChanged, this, &StubMapperDialog::onSyscallSelected); loadSyscalls(); syscallLayout->addWidget(syscallList); syscallGroup->setLayout(syscallLayout); leftLayout->addWidget(syscallGroup); + QWidget* rightPanel = new QWidget(); QVBoxLayout* rightLayout = new QVBoxLayout(rightPanel); + QGroupBox* settingsGroup = new QGroupBox("Stub Configuration"); QVBoxLayout* settingsLayout = new QVBoxLayout(); + currentSyscallLabel = new QLabel("Select a Syscall from the list"); QFont boldFont = currentSyscallLabel->font(); boldFont.setBold(true); currentSyscallLabel->setFont(boldFont); settingsLayout->addWidget(currentSyscallLabel); + settingsTabs = new QTabWidget(); + QWidget* junkTab = new QWidget(); QFormLayout* junkLayout = new QFormLayout(junkTab); + enableJunk = new QCheckBox("Enable Junk Instructions"); connect(enableJunk, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); junkLayout->addRow(enableJunk); + minInstructions = new QSpinBox(); minInstructions->setRange(1, 10); minInstructions->setValue(2); - connect(minInstructions, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(minInstructions, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); junkLayout->addRow("Minimum Instructions:", minInstructions); + maxInstructions = new QSpinBox(); maxInstructions->setRange(1, 20); maxInstructions->setValue(8); - connect(maxInstructions, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(maxInstructions, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); junkLayout->addRow("Maximum Instructions:", maxInstructions); + useAdvancedJunk = new QCheckBox("Advanced Junk Instructions"); connect(useAdvancedJunk, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); junkLayout->addRow(useAdvancedJunk); + QWidget* encryptionTab = new QWidget(); QFormLayout* encryptionLayout = new QFormLayout(encryptionTab); + enableEncryption = new QCheckBox("Enable Syscall ID Encryption"); connect(enableEncryption, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); encryptionLayout->addRow(enableEncryption); + encryptionMethod = new QComboBox(); encryptionMethod->addItem("Basic XOR (Simple)", 1); encryptionMethod->addItem("Multi-Key XOR (Medium)", 2); encryptionMethod->addItem("Add + XOR (Medium)", 3); encryptionMethod->addItem("Enhanced XOR (Medium)", 4); encryptionMethod->addItem("Offset Shifting (Medium)", 5); - connect(encryptionMethod, QOverload::of(&QComboBox::currentIndexChanged), this, &StubMapperDialog::onSettingChanged); + connect(encryptionMethod, QOverload::of(&QComboBox::currentIndexChanged), + this, &StubMapperDialog::onSettingChanged); encryptionLayout->addRow("Encryption Method:", encryptionMethod); + QWidget* structureTab = new QWidget(); QFormLayout* structureLayout = new QFormLayout(structureTab); + enableChunking = new QCheckBox("Enable Function Chunking"); connect(enableChunking, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); structureLayout->addRow(enableChunking); + enableInterleaved = new QCheckBox("Enable Interleaved Execution"); connect(enableInterleaved, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); structureLayout->addRow(enableInterleaved); + shuffleSequence = new QCheckBox("Enable Sequence Shuffling"); connect(shuffleSequence, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); structureLayout->addRow(shuffleSequence); + QWidget* nameTab = new QWidget(); QFormLayout* nameLayout = new QFormLayout(nameTab); QHBoxLayout* syscallNameLayout = new QHBoxLayout(); + syscallPrefixLength = new QSpinBox(); syscallPrefixLength->setRange(4, 16); syscallPrefixLength->setValue(8); - connect(syscallPrefixLength, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(syscallPrefixLength, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); + syscallNumberLength = new QSpinBox(); syscallNumberLength->setRange(4, 16); syscallNumberLength->setValue(6); - connect(syscallNumberLength, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(syscallNumberLength, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); + syscallNameLayout->addWidget(new QLabel("Chars:")); syscallNameLayout->addWidget(syscallPrefixLength); syscallNameLayout->addWidget(new QLabel("Numbers:")); syscallNameLayout->addWidget(syscallNumberLength); nameLayout->addRow("Syscall Name Length:", syscallNameLayout); + offsetNameLength = new QSpinBox(); offsetNameLength->setRange(4, 16); offsetNameLength->setValue(8); - connect(offsetNameLength, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); + connect(offsetNameLength, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); nameLayout->addRow("Offset Name Length:", offsetNameLength); + QWidget* controlFlowTab = new QWidget(); QFormLayout* controlFlowLayout = new QFormLayout(controlFlowTab); + enableControlFlow = new QCheckBox("Enable Control Flow"); connect(enableControlFlow, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); controlFlowLayout->addRow(enableControlFlow); + opaquePredicates = new QCheckBox("Opaque Predicates"); connect(opaquePredicates, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); controlFlowLayout->addRow(opaquePredicates); + bogusControlFlow = new QCheckBox("Bogus Control Flow"); connect(bogusControlFlow, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow(bogusControlFlow); + controlFlowLayout->addRow(bogusControlFlow); + indirectJumps = new QCheckBox("Indirect Jumps"); connect(indirectJumps, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow(indirectJumps); + controlFlowLayout->addRow(indirectJumps); + conditionalBranches = new QCheckBox("Conditional Branches"); connect(conditionalBranches, &QCheckBox::stateChanged, this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow(conditionalBranches); + controlFlowLayout->addRow(conditionalBranches); + controlFlowComplexity = new QSpinBox(); controlFlowComplexity->setRange(1, 10); controlFlowComplexity->setValue(2); - connect(controlFlowComplexity, QOverload::of(&QSpinBox::valueChanged), this, &StubMapperDialog::onSettingChanged); - controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + connect(controlFlowComplexity, QOverload::of(&QSpinBox::valueChanged), + this, &StubMapperDialog::onSettingChanged); + controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + settingsTabs->addTab(junkTab, "Junk Instructions"); settingsTabs->addTab(encryptionTab, "Encryption"); settingsTabs->addTab(structureTab, "Structure"); @@ -152,23 +197,31 @@ void StubMapperDialog::initUI() { settingsLayout->addWidget(settingsTabs); settingsGroup->setLayout(settingsLayout); rightLayout->addWidget(settingsGroup); + splitter->addWidget(leftPanel); splitter->addWidget(rightPanel); splitter->setSizes({300, 500}); layout->addWidget(splitter); + QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->setSpacing(10); buttonLayout->setContentsMargins(20, 10, 20, 10); + useGlobalBtn = new QPushButton("Use Global Settings"); connect(useGlobalBtn, &QPushButton::clicked, this, &StubMapperDialog::useGlobalSettings); + resetBtn = new QPushButton("Reset"); connect(resetBtn, &QPushButton::clicked, this, &StubMapperDialog::resetCurrentSettings); + QPushButton* validateBtn = new QPushButton("Validate"); connect(validateBtn, &QPushButton::clicked, this, &StubMapperDialog::validateCurrentSettings); + QPushButton* saveBtn = new QPushButton("Save"); connect(saveBtn, &QPushButton::clicked, this, &StubMapperDialog::saveSettings); + QPushButton* cancelBtn = new QPushButton("Cancel"); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); + buttonLayout->addWidget(useGlobalBtn); buttonLayout->addWidget(resetBtn); buttonLayout->addWidget(validateBtn); @@ -176,14 +229,18 @@ void StubMapperDialog::initUI() { buttonLayout->addWidget(saveBtn); buttonLayout->addWidget(cancelBtn); layout->addLayout(buttonLayout); + connect(titleBar, &SettingsTitleBar::closeClicked, this, &QDialog::reject); setupStylesheet(); enableControls(false); } -void StubMapperDialog::setupStylesheet() { +void StubMapperDialog::setupStylesheet() +{ QFile stylesheetFile(":/src/GUI/Stylesheets/StubMapperDialog.qss"); - if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) { + + if (stylesheetFile.open(QFile::ReadOnly | QFile::Text)) + { QTextStream in(&stylesheetFile); QString stylesheet = in.readAll(); setStyleSheet(stylesheet); @@ -191,101 +248,143 @@ void StubMapperDialog::setupStylesheet() { } } -void StubMapperDialog::mousePressEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void StubMapperDialog::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = true; m_dragPosition = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } -void StubMapperDialog::mouseMoveEvent(QMouseEvent* event) { - if (event->buttons() & Qt::LeftButton && m_dragging) { +void StubMapperDialog::mouseMoveEvent(QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton && m_dragging) + { move(event->globalPos() - m_dragPosition); event->accept(); } } -void StubMapperDialog::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void StubMapperDialog::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { m_dragging = false; event->accept(); } } -void StubMapperDialog::loadSyscalls() { +void StubMapperDialog::loadSyscalls() +{ syscallList->clear(); QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList(); - if (selectedSyscalls.isEmpty()) { + + if (selectedSyscalls.isEmpty()) + { QString headerPath = PathUtils::getSysCallerPath() + "/Wrapper/include/Sys/sysFunctions.h"; QString syscallMode = settings->value("general/syscall_mode", "Nt").toString(); QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK"; + QFile headerFile(headerPath); - if (headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + + if (headerFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { QTextStream in(&headerFile); - while (!in.atEnd()) { + + while (!in.atEnd()) + { QString line = in.readLine(); QRegularExpression regex(QString(R"(extern "C" (?:NTSTATUS|ULONG) (%1\w+)\()").arg(syscallPrefix)); QRegularExpressionMatch match = regex.match(line); - if (match.hasMatch()) { + + if (match.hasMatch()) + { selectedSyscalls.append(match.captured(1)); } + QRegularExpression scRegex(R"(extern "C" (?:NTSTATUS|ULONG) (SC\w+)\()"); QRegularExpressionMatch scMatch = scRegex.match(line); - if (scMatch.hasMatch()) { + + if (scMatch.hasMatch()) + { QString syscallName = syscallPrefix + scMatch.captured(1).mid(2); selectedSyscalls.append(syscallName); } } + headerFile.close(); } + selectedSyscalls.sort(); } - for (const QString& syscall : selectedSyscalls) { + + for (const QString& syscall : selectedSyscalls) + { QListWidgetItem* item = new QListWidgetItem(syscall); - if (syscallSettings.contains(syscall)) { + + if (syscallSettings.contains(syscall)) + { item->setForeground(Qt::green); } + syscallList->addItem(item); } } -void StubMapperDialog::filterSyscalls(const QString& text) { - for (int i = 0; i < syscallList->count(); ++i) { +void StubMapperDialog::filterSyscalls(const QString& text) +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (item->text().toLower().contains(text.toLower())) { + + if (item->text().toLower().contains(text.toLower())) + { item->setHidden(false); - } else { + } + else + { item->setHidden(true); } } } -void StubMapperDialog::onSyscallSelected(QListWidgetItem* current, QListWidgetItem* previous) { - if (current == nullptr) { +void StubMapperDialog::onSyscallSelected(QListWidgetItem* current, QListWidgetItem* previous) +{ + if (current == nullptr) + { enableControls(false); currentSyscallLabel->setText("Select a Syscall from the list"); return; } + enableControls(true); QString syscallName = current->text(); currentSyscallLabel->setText(QString("Configuring: %1").arg(syscallName)); loadSyscallSpecificSettings(syscallName); } -void StubMapperDialog::loadSyscallSpecificSettings(const QString& syscallName) { - if (syscallSettings.contains(syscallName)) { +void StubMapperDialog::loadSyscallSpecificSettings(const QString& syscallName) +{ + if (syscallSettings.contains(syscallName)) + { QMap settings = syscallSettings[syscallName].toMap(); + enableJunk->setChecked(settings.value("enable_junk", false).toBool()); minInstructions->setValue(settings.value("min_instructions", 2).toInt()); maxInstructions->setValue(settings.value("max_instructions", 8).toInt()); useAdvancedJunk->setChecked(settings.value("use_advanced_junk", false).toBool()); enableEncryption->setChecked(settings.value("enable_encryption", false).toBool()); - int encryptionMethodValue = settings.value("encryption_method", 1).toInt(); + + int encryptionMethodValue = settings.value("encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt(); int index = encryptionMethod->findData(encryptionMethodValue); - if (index >= 0) { + + if (index >= 0) + { encryptionMethod->setCurrentIndex(index); } + enableChunking->setChecked(settings.value("enable_chunking", false).toBool()); enableInterleaved->setChecked(settings.value("enable_interleaved", false).toBool()); shuffleSequence->setChecked(settings.value("shuffle_sequence", false).toBool()); @@ -298,22 +397,29 @@ void StubMapperDialog::loadSyscallSpecificSettings(const QString& syscallName) { indirectJumps->setChecked(settings.value("control_flow_indirect_jumps", false).toBool()); conditionalBranches->setChecked(settings.value("control_flow_conditional_branches", false).toBool()); controlFlowComplexity->setValue(settings.value("control_flow_complexity", 2).toInt()); - } else { + } + else + { loadGlobalSettings(); } } -void StubMapperDialog::loadGlobalSettings() { +void StubMapperDialog::loadGlobalSettings() +{ enableJunk->setChecked(true); minInstructions->setValue(settings->value("obfuscation/min_instructions", 2).toInt()); maxInstructions->setValue(settings->value("obfuscation/max_instructions", 8).toInt()); useAdvancedJunk->setChecked(settings->value("obfuscation/use_advanced_junk", false).toBool()); enableEncryption->setChecked(settings->value("obfuscation/enable_encryption", true).toBool()); - int encryptionMethodValue = settings->value("obfuscation/encryption_method", 1).toInt(); + + int encryptionMethodValue = settings->value("obfuscation/encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt(); int index = encryptionMethod->findData(encryptionMethodValue); - if (index >= 0) { + + if (index >= 0) + { encryptionMethod->setCurrentIndex(index); } + enableChunking->setChecked(settings->value("obfuscation/enable_chunking", true).toBool()); enableInterleaved->setChecked(settings->value("obfuscation/enable_interleaved", true).toBool()); shuffleSequence->setChecked(settings->value("obfuscation/shuffle_sequence", true).toBool()); @@ -328,26 +434,38 @@ void StubMapperDialog::loadGlobalSettings() { controlFlowComplexity->setValue(settings->value("obfuscation/control_flow_complexity", 2).toInt()); } -void StubMapperDialog::useGlobalSettings() { +void StubMapperDialog::useGlobalSettings() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); - if (syscallSettings.contains(syscallName)) { + + if (syscallSettings.contains(syscallName)) + { syscallSettings.remove(syscallName); currentItem->setForeground(Qt::white); } + loadGlobalSettings(); } } -void StubMapperDialog::resetCurrentSettings() { +void StubMapperDialog::resetCurrentSettings() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); - if (syscallSettings.contains(syscallName)) { + + if (syscallSettings.contains(syscallName)) + { syscallSettings.remove(syscallName); currentItem->setForeground(Qt::white); } + enableJunk->setChecked(true); minInstructions->setValue(2); maxInstructions->setValue(8); @@ -369,17 +487,22 @@ void StubMapperDialog::resetCurrentSettings() { } } -void StubMapperDialog::onSettingChanged() { +void StubMapperDialog::onSettingChanged() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); saveCurrentSyscallSettings(syscallName); currentItem->setForeground(Qt::green); } } -void StubMapperDialog::saveCurrentSyscallSettings(const QString& syscallName) { +void StubMapperDialog::saveCurrentSyscallSettings(const QString& syscallName) +{ QMap settings; + settings["enable_junk"] = enableJunk->isChecked(); settings["min_instructions"] = minInstructions->value(); settings["max_instructions"] = maxInstructions->value(); @@ -398,110 +521,157 @@ void StubMapperDialog::saveCurrentSyscallSettings(const QString& syscallName) { settings["control_flow_indirect_jumps"] = indirectJumps->isChecked(); settings["control_flow_conditional_branches"] = conditionalBranches->isChecked(); settings["control_flow_complexity"] = controlFlowComplexity->value(); + syscallSettings[syscallName] = QVariant::fromValue(settings); } -void StubMapperDialog::validateCurrentSettings() { +void StubMapperDialog::validateCurrentSettings() +{ QListWidgetItem* currentItem = syscallList->currentItem(); - if (currentItem) { + + if (currentItem) + { QString syscallName = currentItem->text(); - if (syscallSettings.contains(syscallName)) { + + if (syscallSettings.contains(syscallName)) + { QMap settings = syscallSettings[syscallName].toMap(); QString errorMessage; bool isValid = validateStubSettings(settings, errorMessage); - if (isValid) { + + if (isValid) + { showValidationSuccess(QString("Settings for %1 are Valid!").arg(syscallName)); - } else { + } + else + { showValidationError(QString("Settings for %1 are Invalid: %2").arg(syscallName, errorMessage)); } - } else { + } + else + { showValidationSuccess(QString("Using Global Settings for %1.").arg(syscallName)); } - } else { - QMessageBox::warning(this, "Bind - v1.3.0", "Please select a Syscall first."); + } + else + { + QMessageBox::warning(this, "Bind - v1.3.1", "Please select a Syscall first."); } } -void StubMapperDialog::enableControls(bool enabled) { +void StubMapperDialog::enableControls(bool enabled) +{ settingsTabs->setEnabled(enabled); useGlobalBtn->setEnabled(enabled); resetBtn->setEnabled(enabled); } -void StubMapperDialog::loadSyscallSettings() { +void StubMapperDialog::loadSyscallSettings() +{ syscallSettings = settings->value("stub_mapper/syscall_settings", QMap()).toMap(); } -void StubMapperDialog::saveSettings() { +void StubMapperDialog::saveSettings() +{ QStringList invalidSyscalls; - for (auto it = syscallSettings.begin(); it != syscallSettings.end(); ++it) { + + for (auto it = syscallSettings.begin(); it != syscallSettings.end(); ++it) + { QString syscallName = it.key(); QMap settings = it.value().toMap(); QString errorMessage; bool isValid = validateStubSettings(settings, errorMessage); - if (!isValid) { + + if (!isValid) + { invalidSyscalls.append(QString("%1: %2").arg(syscallName, errorMessage)); } } - if (!invalidSyscalls.isEmpty()) { + + if (!invalidSyscalls.isEmpty()) + { QString errorMessage = "The following Syscalls have Invalid Settings:\n\n" + invalidSyscalls.join("\n"); showValidationError(errorMessage); return; } + settings->setValue("stub_mapper/syscall_settings", QVariant::fromValue(syscallSettings)); - QMessageBox::information(this, "Bind - v1.3.0", "Custom Syscall Settings have been saved successfully."); + QMessageBox::information(this, "Bind - v1.3.1", "Custom Syscall Settings have been saved successfully."); accept(); } -bool StubMapperDialog::validateStubSettings(const QMap& settings, QString& errorMessage) { - QStringList requiredKeys = { +bool StubMapperDialog::validateStubSettings(const QMap& settings, QString& errorMessage) +{ + QStringList requiredKeys = + { "enable_junk", "min_instructions", "max_instructions", "use_advanced_junk", "enable_encryption", "encryption_method", "enable_chunking", "enable_interleaved", "shuffle_sequence", "syscall_prefix_length", "syscall_number_length", "offset_name_length" }; - for (const QString& key : requiredKeys) { - if (!settings.contains(key)) { + + for (const QString& key : requiredKeys) + { + if (!settings.contains(key)) + { errorMessage = QString("Missing Required Setting: %1").arg(key); return false; } } - if (settings["min_instructions"].toInt() < 1 || settings["min_instructions"].toInt() > 10) { + + if (settings["min_instructions"].toInt() < 1 || settings["min_instructions"].toInt() > 10) + { errorMessage = "Minimum Instructions Must Be Between 1 And 10"; return false; } - if (settings["max_instructions"].toInt() < 1 || settings["max_instructions"].toInt() > 20) { + + if (settings["max_instructions"].toInt() < 1 || settings["max_instructions"].toInt() > 20) + { errorMessage = "Maximum Instructions Must Be Between 1 And 20"; return false; } - if (settings["min_instructions"].toInt() > settings["max_instructions"].toInt()) { + + if (settings["min_instructions"].toInt() > settings["max_instructions"].toInt()) + { errorMessage = "Minimum Instructions Cannot Be Greater Than Maximum Instructions"; return false; } - if (settings["syscall_prefix_length"].toInt() < 4 || settings["syscall_prefix_length"].toInt() > 16) { + + if (settings["syscall_prefix_length"].toInt() < 4 || settings["syscall_prefix_length"].toInt() > 16) + { errorMessage = "Syscall Prefix Length Must Be Between 4 And 16"; return false; } - if (settings["syscall_number_length"].toInt() < 4 || settings["syscall_number_length"].toInt() > 16) { + + if (settings["syscall_number_length"].toInt() < 4 || settings["syscall_number_length"].toInt() > 16) + { errorMessage = "Syscall Number Length Must Be Between 4 And 16"; return false; } - if (settings["offset_name_length"].toInt() < 4 || settings["offset_name_length"].toInt() > 16) { + + if (settings["offset_name_length"].toInt() < 4 || settings["offset_name_length"].toInt() > 16) + { errorMessage = "Offset Name Length Must Be Between 4 And 16"; return false; } + QList validEncryptionMethods = {1, 2, 3, 4, 5}; - if (!validEncryptionMethods.contains(settings["encryption_method"].toInt())) { + + if (!validEncryptionMethods.contains(settings["encryption_method"].toInt())) + { errorMessage = QString("Invalid Encryption Method: %1").arg(settings["encryption_method"].toInt()); return false; } + return true; } -void StubMapperDialog::showValidationError(const QString& message) { - QMessageBox::critical(this, "Bind - v1.3.0", message); +void StubMapperDialog::showValidationError(const QString& message) +{ + QMessageBox::critical(this, "Bind - v1.3.1", message); } -void StubMapperDialog::showValidationSuccess(const QString& message) { - QMessageBox::information(this, "Bind - v1.3.0", message); +void StubMapperDialog::showValidationSuccess(const QString& message) +{ + QMessageBox::information(this, "Bind - v1.3.1", message); } diff --git a/Bind/src/GUI/MainWindow.cpp b/Bind/src/GUI/MainWindow.cpp index cf5ef12..acf84d0 100644 --- a/Bind/src/GUI/MainWindow.cpp +++ b/Bind/src/GUI/MainWindow.cpp @@ -22,12 +22,19 @@ #include #include -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), dragPos(0, 0), validatorThread(nullptr), compatibilityThread(nullptr), verificationThread(nullptr), obfuscationThread(nullptr) { - setWindowTitle("Bind - v1.3.0"); +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , dragPos(0, 0) + , validatorThread(nullptr) + , compatibilityThread(nullptr) + , verificationThread(nullptr) + , obfuscationThread(nullptr) +{ + setWindowTitle("Bind - v1.3.1"); setMinimumSize(1400, 900); setWindowFlags(Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground); - + auto* central = new QWidget(); central->setStyleSheet( "QWidget {" @@ -35,292 +42,414 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), dragPos(0, 0), va " border-radius: 15px;" "}" ); + setCentralWidget(central); + auto* mainLayout = new QVBoxLayout(central); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); + titleBar = new TitleBar(this); mainLayout->addWidget(titleBar); + auto* contentLayout = new QHBoxLayout(); contentLayout->setContentsMargins(20, 20, 20, 20); contentLayout->setSpacing(20); mainLayout->addLayout(contentLayout); + leftPanel = new LeftPanel(this); contentLayout->addWidget(leftPanel); + rightPanel = new RightPanel(this); contentLayout->addWidget(rightPanel, 2); + statusBar = new StatusBar(); mainLayout->addWidget(statusBar); + connect(leftPanel, &LeftPanel::settingsButtonClicked, this, &MainWindow::showSettings); connect(leftPanel, &LeftPanel::validationButtonClicked, this, &MainWindow::runValidation); connect(leftPanel, &LeftPanel::compatibilityButtonClicked, this, &MainWindow::runCompatibility); connect(leftPanel, &LeftPanel::verificationButtonClicked, this, &MainWindow::runVerification); connect(leftPanel, &LeftPanel::obfuscationButtonClicked, this, &MainWindow::runObfuscation); + PathUtils::debugPathDetection(); + connect(titleBar, &TitleBar::minimizeClicked, this, &MainWindow::minimizeWindow); connect(titleBar, &TitleBar::maximizeClicked, this, &MainWindow::toggleMaximize); connect(titleBar, &TitleBar::closeClicked, this, &MainWindow::closeWindow); } -MainWindow::~MainWindow() { - if (validatorThread) { +MainWindow::~MainWindow() +{ + if (validatorThread) + { validatorThread->quit(); validatorThread->wait(); delete validatorThread; } - if (compatibilityThread) { + + if (compatibilityThread) + { compatibilityThread->quit(); compatibilityThread->wait(); delete compatibilityThread; } - if (verificationThread) { + + if (verificationThread) + { verificationThread->quit(); verificationThread->wait(); delete verificationThread; } - if (obfuscationThread) { + + if (obfuscationThread) + { obfuscationThread->quit(); obfuscationThread->wait(); delete obfuscationThread; } } -void MainWindow::mousePressEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) { +void MainWindow::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { dragPos = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } -void MainWindow::mouseMoveEvent(QMouseEvent* event) { - if (event->buttons() == Qt::LeftButton) { +void MainWindow::mouseMoveEvent(QMouseEvent* event) +{ + if (event->buttons() == Qt::LeftButton) + { move(event->globalPos() - dragPos); event->accept(); } } -void MainWindow::showSettings() { +void MainWindow::showSettings() +{ SettingsDialog dialog(this); dialog.exec(); } -void MainWindow::saveAllSettings() { - try { +void MainWindow::saveAllSettings() +{ + try + { QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); - } catch (...) { + } + catch (...) + { } } -void MainWindow::runValidation() { - if (validatorThread && validatorThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Validation Check is already running. Please wait for it to complete."); +void MainWindow::runValidation() +{ + if (validatorThread && validatorThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Validation Check is already running. Please wait for it to complete."); return; } + QStringList dllPaths = leftPanel->getDllPaths(); - if (dllPaths.isEmpty()) { - QMessageBox::warning(this, "Bind - v1.3.0", "No DLL Paths specified. Please add at least one NTDLL path."); + + if (dllPaths.isEmpty()) + { + QMessageBox::warning(this, "Bind - v1.3.1", "No DLL Paths specified. Please add at least one NTDLL path."); return; } - if (validatorThread) { + + if (validatorThread) + { validatorThread->deleteLater(); } + validatorThread = new ValidatorThread(this); validatorThread->setDllPaths(dllPaths); - connect(validatorThread, &ValidatorThread::validationStarted, this, [this]() { + + connect(validatorThread, &ValidatorThread::validationStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Validation Started..."); statusBar->updateStatus("Running Validation Check..."); rightPanel->getOutputPanel()->clearText(); }); - connect(validatorThread, &ValidatorThread::progressUpdated, this, [this](const QString& status) { + + connect(validatorThread, &ValidatorThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(validatorThread, &ValidatorThread::validationFinished, this, [this](bool success, const QString& message) { + + connect(validatorThread, &ValidatorThread::validationFinished, this, [this](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - - if (success) { + + if (success) + { leftPanel->updateStatus("Validation Completed!"); statusBar->updateStatus("Validation Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Validation Failed!"); statusBar->updateStatus("Validation Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + validatorThread->deleteLater(); validatorThread = nullptr; }); + validatorThread->start(); } -void MainWindow::minimizeWindow() { +void MainWindow::minimizeWindow() +{ showMinimized(); } -void MainWindow::toggleMaximize() { - if (isMaximized()) { +void MainWindow::toggleMaximize() +{ + if (isMaximized()) + { showNormal(); - } else { + } + else + { showMaximized(); } } -void MainWindow::closeWindow() { +void MainWindow::closeWindow() +{ close(); QApplication::quit(); } -void MainWindow::runCompatibility() { - if (compatibilityThread && compatibilityThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Compatibility Check is already running. Please wait for it to complete."); +void MainWindow::runCompatibility() +{ + if (compatibilityThread && compatibilityThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Compatibility Check is already running. Please wait for it to complete."); return; } + QStringList dllPaths = leftPanel->getDllPaths(); - if (dllPaths.isEmpty()) { - QMessageBox::warning(this, "Bind - v1.3.0", "No DLL Paths specified. Please add at least one NTDLL path."); + + if (dllPaths.isEmpty()) + { + QMessageBox::warning(this, "Bind - v1.3.1", "No DLL Paths specified. Please add at least one NTDLL path."); return; } - if (compatibilityThread) { + + if (compatibilityThread) + { compatibilityThread->deleteLater(); } + compatibilityThread = new CompatibilityThread(this); compatibilityThread->setDllPaths(dllPaths); - connect(compatibilityThread, &CompatibilityThread::compatibilityStarted, this, [this]() { + + connect(compatibilityThread, &CompatibilityThread::compatibilityStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Compatibility Started..."); statusBar->updateStatus("Running Compatibility Check..."); rightPanel->getOutputPanel()->clearText(); }); - connect(compatibilityThread, &CompatibilityThread::progressUpdated, this, [this](const QString& status) { + + connect(compatibilityThread, &CompatibilityThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(compatibilityThread, &CompatibilityThread::compatibilityFinished, this, [this](bool success, const QString& message) { + + connect(compatibilityThread, &CompatibilityThread::compatibilityFinished, this, [this](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - - if (success) { + + if (success) + { leftPanel->updateStatus("Compatibility Completed!"); statusBar->updateStatus("Compatibility Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Compatibility Failed!"); statusBar->updateStatus("Compatibility Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + compatibilityThread->deleteLater(); compatibilityThread = nullptr; }); + compatibilityThread->start(); } -void MainWindow::runVerification() { - if (verificationThread && verificationThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Verification Check is already running. Please wait for it to complete."); +void MainWindow::runVerification() +{ + if (verificationThread && verificationThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Verification Check is already running. Please wait for it to complete."); return; } + QStringList dllPaths = leftPanel->getDllPaths(); - if (dllPaths.isEmpty()) { - QMessageBox::warning(this, "Bind - v1.3.0", "No DLL Paths specified. Please add at least one NTDLL path."); + + if (dllPaths.isEmpty()) + { + QMessageBox::warning(this, "Bind - v1.3.1", "No DLL Paths specified. Please add at least one NTDLL path."); return; } - if (verificationThread) { + + if (verificationThread) + { verificationThread->deleteLater(); } + verificationThread = new VerificationThread(this); verificationThread->setDllPaths(dllPaths); - verificationThread->setOutputCallback([this](const QString& message) { + + verificationThread->setOutputCallback([this](const QString& message) + { leftPanel->updateStatus(message); statusBar->updateStatus(message); rightPanel->getOutputPanel()->appendText(message); }); - connect(verificationThread, &VerificationThread::verificationStarted, this, [this]() { + + connect(verificationThread, &VerificationThread::verificationStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Verification Started..."); statusBar->updateStatus("Running Verification Check..."); rightPanel->getOutputPanel()->clearText(); }); - connect(verificationThread, &VerificationThread::progressUpdated, this, [this](const QString& status) { + + connect(verificationThread, &VerificationThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(verificationThread, &VerificationThread::verificationFinished, this, [this](bool success, const QString& message) { + + connect(verificationThread, &VerificationThread::verificationFinished, this, [this](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - if (success) { + + if (success) + { leftPanel->updateStatus("Verification Completed!"); statusBar->updateStatus("Verification Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Verification Failed!"); statusBar->updateStatus("Verification Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + verificationThread->deleteLater(); verificationThread = nullptr; }); + verificationThread->start(); } -void MainWindow::runObfuscation() { - if (obfuscationThread && obfuscationThread->isRunning()) { - QMessageBox::information(this, "Bind - v1.3.0", "Syscall Obfuscation is already running. Please wait for it to complete."); +void MainWindow::runObfuscation() +{ + if (obfuscationThread && obfuscationThread->isRunning()) + { + QMessageBox::information(this, "Bind - v1.3.1", "Syscall Obfuscation is already running. Please wait for it to complete."); return; } + saveAllSettings(); + ObfuscationSelectionDialog dialog(this); - if (dialog.exec() != QDialog::Accepted) { + + if (dialog.exec() != QDialog::Accepted) + { return; } + QString selectedMethod; ObfuscationSelectionDialog::Selection selection = dialog.getSelection(); - if (selection == ObfuscationSelectionDialog::NormalObfuscation) { + + if (selection == ObfuscationSelectionDialog::NormalObfuscation) + { selectedMethod = "normal"; QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); settings.setValue("obfuscation/force_normal", true); settings.setValue("obfuscation/force_stub_mapper", false); settings.setValue("obfuscation/last_method", "normal"); - } else if (selection == ObfuscationSelectionDialog::StubMapper) { + } + else if (selection == ObfuscationSelectionDialog::StubMapper) + { selectedMethod = "stub_mapper"; QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); settings.setValue("obfuscation/force_normal", false); settings.setValue("obfuscation/force_stub_mapper", true); settings.setValue("obfuscation/last_method", "stub_mapper"); - } else { + } + else + { return; } - if (obfuscationThread) { + + if (obfuscationThread) + { obfuscationThread->deleteLater(); } + obfuscationThread = new ObfuscationThread(this); - connect(obfuscationThread, &ObfuscationThread::obfuscationStarted, this, [this]() { + + connect(obfuscationThread, &ObfuscationThread::obfuscationStarted, this, [this]() + { leftPanel->setProgressIndeterminate(true); leftPanel->updateStatus("Obfuscation Started..."); statusBar->updateStatus("Running Syscall Obfuscation..."); rightPanel->getOutputPanel()->clearText(); }); - connect(obfuscationThread, &ObfuscationThread::progressUpdated, this, [this](const QString& status) { + + connect(obfuscationThread, &ObfuscationThread::progressUpdated, this, [this](const QString& status) + { leftPanel->updateStatus(status); statusBar->updateStatus(status); rightPanel->getOutputPanel()->appendText(status); }); - connect(obfuscationThread, &ObfuscationThread::obfuscationFinished, this, [this, selectedMethod](bool success, const QString& message) { + + connect(obfuscationThread, &ObfuscationThread::obfuscationFinished, this, [this, selectedMethod](bool success, const QString& message) + { leftPanel->setProgressIndeterminate(false); - if (success) { + + if (success) + { leftPanel->updateStatus("Obfuscation Completed!"); statusBar->updateStatus("Obfuscation Completed Successfully!"); - } else { + } + else + { leftPanel->updateStatus("Obfuscation Failed!"); statusBar->updateStatus("Obfuscation Failed!"); - QMessageBox::critical(this, "Bind - v1.3.0", message); + QMessageBox::critical(this, "Bind - v1.3.1", message); } + QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); settings.remove("obfuscation/force_normal"); settings.remove("obfuscation/force_stub_mapper"); + obfuscationThread->deleteLater(); obfuscationThread = nullptr; }); + obfuscationThread->start(); } -void MainWindow::closeEvent(QCloseEvent* event) { -} +void MainWindow::closeEvent(QCloseEvent* event) +{} diff --git a/Bind/src/GUI/Panels/LeftPanel.cpp b/Bind/src/GUI/Panels/LeftPanel.cpp index 6a761e1..f540d89 100644 --- a/Bind/src/GUI/Panels/LeftPanel.cpp +++ b/Bind/src/GUI/Panels/LeftPanel.cpp @@ -14,7 +14,9 @@ #include #include -LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { +LeftPanel::LeftPanel(QWidget* parent) + : QFrame(parent) +{ setMaximumWidth(350); setStyleSheet( "QFrame {" @@ -25,16 +27,20 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { auto* layout = new QVBoxLayout(this); layout->setContentsMargins(20, 20, 20, 20); layout->setSpacing(10); + auto* topSection = new QVBoxLayout(); topSection->setSpacing(5); topSection->setAlignment(Qt::AlignCenter); + logoImage = new QLabel(this); - logoImage->setPixmap(QPixmap(":/src/Res/Icons/syscaller.png").scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + logoImage->setPixmap(QPixmap(":/src/Res/Icons/syscaller.png") + .scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); logoImage->setFixedSize(128, 128); logoImage->setAlignment(Qt::AlignCenter); logoImage->setStyleSheet("QLabel { background: transparent; }"); logoImage->setContentsMargins(0, 0, 0, 0); topSection->addWidget(logoImage, 0, Qt::AlignCenter); + logoLabel = new QLabel("SysCaller: Bind", this); logoLabel->setStyleSheet( "color: #0077d4;" @@ -45,13 +51,15 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { ); logoLabel->setAlignment(Qt::AlignCenter); topSection->addWidget(logoLabel, 0, Qt::AlignCenter); - versionLabel = new QLabel("v1.3.0", this); + + versionLabel = new QLabel("v1.3.1", this); versionLabel->setStyleSheet("color: #666666; font-size: 12px;"); versionLabel->setAlignment(Qt::AlignCenter); versionLabel->setCursor(Qt::PointingHandCursor); versionLabel->setTextFormat(Qt::RichText); - versionLabel->setText("v1.3.0"); + versionLabel->setText("v1.3.1"); topSection->addWidget(versionLabel, 0, Qt::AlignCenter); + layout->addLayout(topSection); layout->addSpacing(15); auto* dllFrame = new QFrame(this); @@ -62,16 +70,20 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { " padding: 10px;" "}" ); + auto* dllLayout = new QVBoxLayout(dllFrame); dllLayout->setContentsMargins(15, 15, 15, 15); dllLayout->setSpacing(8); + auto* headerBtnLayout = new QHBoxLayout(); auto* dllHeader = new QLabel("NTDLL PATHS", this); dllHeader->setStyleSheet("color: #888888; font-size: 12px; font-weight: bold;"); headerBtnLayout->addWidget(dllHeader); headerBtnLayout->addStretch(); + auto* btnLayout = new QHBoxLayout(); btnLayout->setSpacing(8); + addDllBtn = new QPushButton("Add DLL", this); addDllBtn->setMaximumWidth(100); addDllBtn->setMinimumHeight(26); @@ -93,6 +105,7 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "}" ); btnLayout->addWidget(addDllBtn); + removeDllBtn = new QPushButton("Remove", this); removeDllBtn->setMaximumWidth(80); removeDllBtn->setMinimumHeight(26); @@ -114,9 +127,11 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "}" ); btnLayout->addWidget(removeDllBtn); + headerBtnLayout->addLayout(btnLayout); dllLayout->addLayout(headerBtnLayout); dllLayout->addSpacing(3); + dllList = new QListWidget(this); dllList->setStyleSheet( "QListWidget {" @@ -141,24 +156,27 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { ); dllList->setFixedHeight(90); dllList->setContextMenuPolicy(Qt::CustomContextMenu); + dllLayout->addWidget(dllList); dllFrame->setFixedHeight(175); layout->addWidget(dllFrame); layout->addSpacing(15); auto* buttonsSection = new QVBoxLayout(); buttonsSection->setSpacing(10); + validateBtn = new BindButton( - " Validation Check", - ":/src/Res/Icons/validation.svg", + " Validation Check", + ":/src/Res/Icons/validation.png", "Bind Validation", "Analyzes and updates syscall offsets in syscaller.asm by comparing against ntdll.dll.

" "• Disassembles ntdll.dll exports to extract syscall IDs and ensures correct mapping
" "• Updates or removes syscalls based on their presence in the current systems ntdll.dll" ); buttonsSection->addWidget(validateBtn); + compatibilityBtn = new BindButton( - " Compatibility Check", - ":/src/Res/Icons/compatibility.svg", + " Compatibility Check", + ":/src/Res/Icons/compatibility.png", "Bind Compatibility", "Performs compatibility analysis of syscalls against ntdll.dll:

" "• Detects duplicate syscall names and offsets
" @@ -167,9 +185,10 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "• Reports valid, invalid, and duplicate syscalls with detailed status" ); buttonsSection->addWidget(compatibilityBtn); + verifyBtn = new BindButton( - " Verification Check", - ":/src/Res/Icons/verification.svg", + " Verification Check", + ":/src/Res/Icons/verification.png", "Bind Verification", "Performs comprehensive syscall verification:

" "• Validates return types (NTSTATUS, BOOL, HANDLE, etc.)
" @@ -178,9 +197,10 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "• Traces type definitions in header files" ); buttonsSection->addWidget(verifyBtn); + obfuscateBtn = new BindButton( - " Obfuscation", - ":/src/Res/Icons/obfuscation.svg", + " Obfuscation", + ":/src/Res/Icons/obfuscation.png", "Bind Obfuscation", "Obfuscates syscalls to enhance protection:

" "• Randomizes syscall names and offsets
" @@ -189,13 +209,15 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { "• Preserves original syscall functionality" ); buttonsSection->addWidget(obfuscateBtn); + settingsBtn = new BindButton( " Settings", - ":/src/Res/Icons/settings.svg", + ":/src/Res/Icons/settings.png", "Bind Settings", "Configure SysCaller project settings" ); buttonsSection->addWidget(settingsBtn); + layout->addLayout(buttonsSection); layout->addSpacing(10); auto* statusFrame = new QFrame(this); @@ -206,21 +228,28 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { " padding: 10px;" "}" ); + auto* statusLayout = new QVBoxLayout(statusFrame); statusLayout->setContentsMargins(15, 15, 15, 15); + progressBar = new ProgressBar(this); statusLayout->addWidget(progressBar); + statusLabel = new QLabel("Ready", this); statusLabel->setStyleSheet( "color: #666666; font-size: 12px; padding: 5px; border-radius: 5px; background: rgba(102, 102, 102, 0.1);" ); statusLabel->setAlignment(Qt::AlignCenter); statusLayout->addWidget(statusLabel); + layout->addWidget(statusFrame); layout->addStretch(); + dllList->addItem("C:\\Windows\\System32\\ntdll.dll"); + connect(addDllBtn, &QPushButton::clicked, this, &LeftPanel::browseDll); emit dllPathsChanged(getDllPaths()); + connect(removeDllBtn, &QPushButton::clicked, this, &LeftPanel::removeSelectedDll); connect(dllList, &QListWidget::customContextMenuRequested, this, &LeftPanel::showContextMenu); connect(settingsBtn, &BindButton::clicked, this, &LeftPanel::settingsButtonClicked); @@ -231,43 +260,60 @@ LeftPanel::LeftPanel(QWidget* parent) : QFrame(parent) { connect(versionLabel, &QLabel::linkActivated, this, &LeftPanel::showChangelogDialog); } -void LeftPanel::browseDll() { +void LeftPanel::browseDll() +{ QString dllPath = QFileDialog::getOpenFileName( this, - "Bind - v1.3.0", + "Bind - v1.3.1", "", "DLL Files (*.dll);;All Files (*.*)" ); - if (!dllPath.isEmpty()) { + + if (!dllPath.isEmpty()) + { bool exists = false; - for (int i = 0; i < dllList->count(); ++i) { - if (dllList->item(i)->text() == dllPath) { + + for (int i = 0; i < dllList->count(); ++i) + { + if (dllList->item(i)->text() == dllPath) + { exists = true; break; } } - if (!exists) { + + if (!exists) + { dllList->addItem(dllPath); emit dllPathsChanged(getDllPaths()); } } } -void LeftPanel::removeSelectedDll() { +void LeftPanel::removeSelectedDll() +{ QList selectedItems = dllList->selectedItems(); - if (selectedItems.isEmpty() || dllList->count() <= 1) { + + if (selectedItems.isEmpty() || dllList->count() <= 1) + { return; } - for (QListWidgetItem* item : selectedItems) { + + for (QListWidgetItem* item : selectedItems) + { delete dllList->takeItem(dllList->row(item)); } + emit dllPathsChanged(getDllPaths()); } -void LeftPanel::showContextMenu(const QPoint& pos) { - if (dllList->count() <= 1) { +void LeftPanel::showContextMenu(const QPoint& pos) +{ + if (dllList->count() <= 1) + { return; } + QMenu menu(this); menu.setStyleSheet( "QMenu {" @@ -282,35 +328,48 @@ void LeftPanel::showContextMenu(const QPoint& pos) { " background-color: #4880a8;" "}" ); + QAction* removeAction = menu.addAction("Remove"); QAction* selectedAction = menu.exec(dllList->mapToGlobal(pos)); - if (selectedAction == removeAction) { + + if (selectedAction == removeAction) + { removeSelectedDll(); } } -QStringList LeftPanel::getDllPaths() const { +QStringList LeftPanel::getDllPaths() const +{ QStringList paths; - for (int i = 0; i < dllList->count(); ++i) { + + for (int i = 0; i < dllList->count(); ++i) + { paths.append(dllList->item(i)->text()); } + return paths; } -void LeftPanel::setProgressIndeterminate(bool indeterminate) { - if (indeterminate) { +void LeftPanel::setProgressIndeterminate(bool indeterminate) +{ + if (indeterminate) + { progressBar->setRange(0, 0); - } else { + } + else + { progressBar->setRange(0, 1); progressBar->setValue(1); } } -void LeftPanel::updateStatus(const QString& message) { +void LeftPanel::updateStatus(const QString& message) +{ statusLabel->setText(message); } -void LeftPanel::showChangelogDialog() { +void LeftPanel::showChangelogDialog() +{ ChangelogDialog dialog(this); dialog.exec(); } diff --git a/Bind/src/GUI/Panels/OutputPanel.cpp b/Bind/src/GUI/Panels/OutputPanel.cpp index bf6d971..9354583 100644 --- a/Bind/src/GUI/Panels/OutputPanel.cpp +++ b/Bind/src/GUI/Panels/OutputPanel.cpp @@ -1,6 +1,8 @@ #include "include/GUI/Panels/OutputPanel.h" -OutputPanel::OutputPanel(QWidget* parent) : QTextEdit(parent) { +OutputPanel::OutputPanel(QWidget* parent) + : QTextEdit(parent) +{ setReadOnly(true); setStyleSheet( "QTextEdit {" @@ -13,13 +15,15 @@ OutputPanel::OutputPanel(QWidget* parent) : QTextEdit(parent) { ); } -void OutputPanel::appendText(const QString& text) { +void OutputPanel::appendText(const QString& text) +{ append(text); QTextCursor cursor = textCursor(); cursor.movePosition(QTextCursor::End); setTextCursor(cursor); } -void OutputPanel::clearText() { +void OutputPanel::clearText() +{ clear(); } \ No newline at end of file diff --git a/Bind/src/GUI/Panels/RightPanel.cpp b/Bind/src/GUI/Panels/RightPanel.cpp index e7ed3c6..7e54d9b 100644 --- a/Bind/src/GUI/Panels/RightPanel.cpp +++ b/Bind/src/GUI/Panels/RightPanel.cpp @@ -3,28 +3,35 @@ #include #include -RightPanel::RightPanel(QWidget* parent) : QFrame(parent) { +RightPanel::RightPanel(QWidget* parent) + : QFrame(parent) +{ setStyleSheet( "QFrame {" " background: #252525;" " border-radius: 15px;" "}" ); + auto* layout = new QVBoxLayout(this); layout->setContentsMargins(20, 20, 20, 20); + headerLabel = new QLabel("Bind Console", this); headerLabel->setStyleSheet( "color: #0077d4; font-size: 16px; font-weight: bold; padding: 10px; background: rgba(72, 128, 168, 0.2); border-radius: 8px;" ); layout->addWidget(headerLabel); + outputText = new OutputPanel(this); layout->addWidget(outputText); } -void RightPanel::appendOutput(const QString& text) { +void RightPanel::appendOutput(const QString& text) +{ outputText->appendText(text); } -void RightPanel::clearOutput() { +void RightPanel::clearOutput() +{ outputText->clearText(); } \ No newline at end of file diff --git a/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp b/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp index 92682ef..c8da96a 100644 --- a/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/GeneralTab.cpp @@ -12,44 +12,56 @@ #include #include -GeneralTab::GeneralTab(QSettings* settings, QWidget* parent) - : QWidget(parent), - settings(settings), - modeButtonGroup(nullptr), - ntModeRadio(nullptr), - zwModeRadio(nullptr), - bindingsButtonGroup(nullptr), - bindingsEnableRadio(nullptr), - bindingsDisableRadio(nullptr), - bindingsGroup(nullptr), - inlineAssemblyGroup(nullptr), - assemblyModeGroup(nullptr), - directAssemblyRadio(nullptr), - inlineAssemblyRadio(nullptr), - indirectAssemblyRadio(nullptr), - hashStubs(nullptr), - createBackup(nullptr) { +GeneralTab::GeneralTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) + , modeButtonGroup(nullptr) + , ntModeRadio(nullptr) + , zwModeRadio(nullptr) + , bindingsButtonGroup(nullptr) + , bindingsEnableRadio(nullptr) + , bindingsDisableRadio(nullptr) + , bindingsGroup(nullptr) + , inlineAssemblyGroup(nullptr) + , assemblyModeGroup(nullptr) + , directAssemblyRadio(nullptr) + , inlineAssemblyRadio(nullptr) + , indirectAssemblyRadio(nullptr) + , hashStubs(nullptr) + , createBackup(nullptr) +{ initUI(); } -void GeneralTab::initUI() { +void GeneralTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); QGroupBox* syscallModeGroup = new QGroupBox("Syscall Mode"); QVBoxLayout* syscallModeLayout = new QVBoxLayout(); + QLabel* description = new QLabel("Select which syscall mode to use. This affects how syscalls are generated and processed."); description->setWordWrap(true); syscallModeLayout->addWidget(description); + modeButtonGroup = new QButtonGroup(this); + ntModeRadio = new QRadioButton("Nt Mode (User Mode)"); ntModeRadio->setToolTip("Use Nt prefix for syscalls (default for user-mode applications)"); + zwModeRadio = new QRadioButton("Zw Mode (Kernel Mode)"); zwModeRadio->setToolTip("Use Zw prefix for syscalls (primarily used in kernel-mode drivers)"); + originalMode = settings->value("general/syscall_mode", "Nt").toString(); - if (originalMode == "Zw") { + + if (originalMode == "Zw") + { zwModeRadio->setChecked(true); - } else { + } + else + { ntModeRadio->setChecked(true); } + modeButtonGroup->addButton(ntModeRadio); modeButtonGroup->addButton(zwModeRadio); syscallModeLayout->addWidget(ntModeRadio); @@ -58,58 +70,83 @@ void GeneralTab::initUI() { layout->addWidget(syscallModeGroup); connect(ntModeRadio, &QRadioButton::toggled, this, &GeneralTab::onModeChanged); connect(zwModeRadio, &QRadioButton::toggled, this, &GeneralTab::onModeChanged); + bindingsGroup = new QGroupBox("Bindings"); QVBoxLayout* bindingsLayout = new QVBoxLayout(); + QLabel* bindingsDesc = new QLabel("Enable or disable automatic generation of SysCaller.def for bindings."); bindingsDesc->setWordWrap(true); bindingsLayout->addWidget(bindingsDesc); + bindingsButtonGroup = new QButtonGroup(this); + bindingsEnableRadio = new QRadioButton("Enable"); bindingsDisableRadio = new QRadioButton("Disable"); + bindingsButtonGroup->addButton(bindingsEnableRadio); bindingsButtonGroup->addButton(bindingsDisableRadio); bindingsLayout->addWidget(bindingsEnableRadio); bindingsLayout->addWidget(bindingsDisableRadio); bindingsGroup->setLayout(bindingsLayout); layout->addWidget(bindingsGroup); + bool bindingsEnabled = settings->value("general/bindings_enabled", false).toBool(); - if (bindingsEnabled) { + + if (bindingsEnabled) + { bindingsEnableRadio->setChecked(true); - } else { + } + else + { bindingsDisableRadio->setChecked(true); } - inlineAssemblyGroup = new QGroupBox("Assembly Mode"); + inlineAssemblyGroup = new QGroupBox("Syscall Mode"); QVBoxLayout* inlineAssemblyLayout = new QVBoxLayout(); - QLabel* inlineDesc = new QLabel("Select the assembly mode for syscall generation."); + + QLabel* inlineDesc = new QLabel("Select a mode for syscall generation."); inlineDesc->setWordWrap(true); inlineAssemblyLayout->addWidget(inlineDesc); + QButtonGroup* assemblyModeGroup = new QButtonGroup(this); - QRadioButton* directAssemblyRadio = new QRadioButton("Direct Assembly Mode (Default)"); + + QRadioButton* directAssemblyRadio = new QRadioButton("Direct Syscall Mode (Default)"); directAssemblyRadio->setToolTip("Use traditional instruction mnemonics for syscall stubs"); - QRadioButton* inlineAssemblyRadio = new QRadioButton("Inline Assembly Mode"); + + QRadioButton* inlineAssemblyRadio = new QRadioButton("Inline Syscall Mode"); inlineAssemblyRadio->setToolTip("Generate MASM compatible db-based stubs"); - QRadioButton* indirectAssemblyRadio = new QRadioButton("Indirect Assembly Mode"); + + QRadioButton* indirectAssemblyRadio = new QRadioButton("Indirect Syscall Mode"); indirectAssemblyRadio->setToolTip("Generate runtime syscall resolution stubs for enhanced stealth"); + assemblyModeGroup->addButton(directAssemblyRadio); assemblyModeGroup->addButton(inlineAssemblyRadio); assemblyModeGroup->addButton(indirectAssemblyRadio); + bool inlineEnabled = settings->value("general/inline_assembly", false).toBool(); bool indirectEnabled = settings->value("general/indirect_assembly", false).toBool(); - - if (inlineEnabled) { + + if (inlineEnabled) + { inlineAssemblyRadio->setChecked(true); - } else if (indirectEnabled) { + } + else if (indirectEnabled) + { indirectAssemblyRadio->setChecked(true); - } else { + } + else + { directAssemblyRadio->setChecked(true); } + inlineAssemblyLayout->addWidget(directAssemblyRadio); inlineAssemblyLayout->addWidget(inlineAssemblyRadio); inlineAssemblyLayout->addWidget(indirectAssemblyRadio); + this->directAssemblyRadio = directAssemblyRadio; this->inlineAssemblyRadio = inlineAssemblyRadio; this->indirectAssemblyRadio = indirectAssemblyRadio; this->assemblyModeGroup = assemblyModeGroup; + connect(directAssemblyRadio, &QRadioButton::toggled, this, &GeneralTab::onAssemblyModeChanged); connect(inlineAssemblyRadio, &QRadioButton::toggled, this, &GeneralTab::onAssemblyModeChanged); connect(indirectAssemblyRadio, &QRadioButton::toggled, this, &GeneralTab::onAssemblyModeChanged); @@ -117,94 +154,147 @@ void GeneralTab::initUI() { layout->addWidget(inlineAssemblyGroup); QGroupBox* hashStubsGroup = new QGroupBox("Hash Stubs"); QVBoxLayout* hashStubsLayout = new QVBoxLayout(); + QLabel* hashDesc = new QLabel("Optionally hash each stub/build with unique hash for future lookups."); hashDesc->setWordWrap(true); hashStubsLayout->addWidget(hashDesc); + QPushButton* hashCompareBtn = new QPushButton("Hash Compare"); hashCompareBtn->setToolTip("Compare hash files from different builds to identify changes"); connect(hashCompareBtn, &QPushButton::clicked, this, &GeneralTab::openHashCompare); hashStubsLayout->addWidget(hashCompareBtn); + hashStubs = new QCheckBox("Enable Hash Stubs"); hashStubs->setChecked(settings->value("general/hash_stubs", false).toBool()); hashStubs->setToolTip("If checked, will generate hashes for all stubs after obfuscation and save them to a JSON file in the Backups directory"); hashStubsLayout->addWidget(hashStubs); hashStubsGroup->setLayout(hashStubsLayout); layout->addWidget(hashStubsGroup); + QGroupBox* resetGroup = new QGroupBox("Reset to Default/Backup"); QVBoxLayout* resetLayout = new QVBoxLayout(); + QLabel* resetDesc = new QLabel("Reset SysCaller to it's default state or restore from a backup. This will revert any changes made by obfuscation or manual editing."); resetDesc->setWordWrap(true); resetLayout->addWidget(resetDesc); + QPushButton* restoreBtn = new QPushButton("Restore Files"); restoreBtn->setMinimumHeight(40); connect(restoreBtn, &QPushButton::clicked, this, &GeneralTab::showRestoreOptions); resetLayout->addWidget(restoreBtn); + createBackup = new QCheckBox("Create Backup"); createBackup->setChecked(settings->value("general/create_backup", true).toBool()); createBackup->setToolTip("If checked, will create backup files in the Backups directory before restoring defaults"); resetLayout->addWidget(createBackup); resetGroup->setLayout(resetLayout); layout->addWidget(resetGroup); + onModeChanged(); } -void GeneralTab::onModeChanged() { +void GeneralTab::onModeChanged() +{ bool isKernelMode = zwModeRadio->isChecked(); bindingsGroup->setVisible(!isKernelMode); inlineAssemblyGroup->setVisible(!isKernelMode); - if (isKernelMode && bindingsEnableRadio->isChecked()) { + + if (isKernelMode && bindingsEnableRadio->isChecked()) + { bindingsDisableRadio->setChecked(true); } - if (isKernelMode) { - if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) { + + if (isKernelMode) + { + if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) + { directAssemblyRadio->setChecked(true); } - if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) { + + if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) + { directAssemblyRadio->setChecked(true); } - if (inlineAssemblyRadio) inlineAssemblyRadio->setEnabled(false); - if (indirectAssemblyRadio) indirectAssemblyRadio->setEnabled(false); - if (directAssemblyRadio) directAssemblyRadio->setEnabled(true); - } else { - if (inlineAssemblyRadio) inlineAssemblyRadio->setEnabled(true); - if (indirectAssemblyRadio) indirectAssemblyRadio->setEnabled(true); - if (directAssemblyRadio) directAssemblyRadio->setEnabled(true); + + if (inlineAssemblyRadio) + { + inlineAssemblyRadio->setEnabled(false); + } + + if (indirectAssemblyRadio) + { + indirectAssemblyRadio->setEnabled(false); + } + + if (directAssemblyRadio) + { + directAssemblyRadio->setEnabled(true); + } + } + else + { + if (inlineAssemblyRadio) + { + inlineAssemblyRadio->setEnabled(true); + } + + if (indirectAssemblyRadio) + { + indirectAssemblyRadio->setEnabled(true); + } + + if (directAssemblyRadio) + { + directAssemblyRadio->setEnabled(true); + } } } void GeneralTab::onAssemblyModeChanged() { } -void GeneralTab::saveSettings() { +void GeneralTab::saveSettings() +{ settings->setValue("general/create_backup", createBackup->isChecked()); settings->setValue("general/hash_stubs", hashStubs->isChecked()); settings->setValue("general/bindings_enabled", bindingsEnableRadio->isChecked()); - if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) { + + if (inlineAssemblyRadio && inlineAssemblyRadio->isChecked()) + { settings->setValue("general/inline_assembly", true); settings->setValue("general/indirect_assembly", false); - } else if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) { + } + else if (indirectAssemblyRadio && indirectAssemblyRadio->isChecked()) + { settings->setValue("general/inline_assembly", false); settings->setValue("general/indirect_assembly", true); - } else { + } + else + { settings->setValue("general/inline_assembly", false); settings->setValue("general/indirect_assembly", false); } + QString newMode = zwModeRadio->isChecked() ? "Zw" : "Nt"; bool modeChanged = newMode != originalMode; settings->setValue("general/syscall_mode", newMode); - if (modeChanged) { - ConfirmationDialog infoDialog("Bind - v1.3.0", this); + + if (modeChanged) + { + ConfirmationDialog infoDialog("Bind - v1.3.1", this); infoDialog.setMessage(QString("The syscall mode has been changed from %1 to %2.\n\n" "This change affects which files are processed:\n" "- Nt Mode: User mode files in SysCaller directory\n" "- Zw Mode: Kernel mode files in SysCallerK directory\n\n" - "Some changes may take full effect after a restart.").arg(originalMode, newMode)); + "Some changes may take full effect after a restart.") + .arg(originalMode, newMode)); infoDialog.setButtons(false, false, true, false); infoDialog.exec(); } } -void GeneralTab::showRestoreOptions() { +void GeneralTab::showRestoreOptions() +{ QMenu menu(this); menu.setStyleSheet( "QMenu {" @@ -223,82 +313,117 @@ void GeneralTab::showRestoreOptions() { " background-color: #0b5394;" "}" ); + QAction* defaultAction = menu.addAction("Restore Default Files"); connect(defaultAction, &QAction::triggered, this, &GeneralTab::restoreDefaultFiles); + QStringList completeBackups = getAvailableBackups(); - if (!completeBackups.isEmpty()) { - if (completeBackups.size() > 1) { + + if (!completeBackups.isEmpty()) + { + if (completeBackups.size() > 1) + { QString latestTs = completeBackups.first(); QString latestDate = formatTimestamp(latestTs); + QAction* latestAction = menu.addAction( QString("Restore Latest Backup (%1)").arg(latestDate)); - connect(latestAction, &QAction::triggered, [this, latestTs]() { + connect(latestAction, &QAction::triggered, [this, latestTs]() + { restoreBackup(latestTs); }); + QMenu* backupSubmenu = new QMenu("Select Backup", &menu); backupSubmenu->setStyleSheet(menu.styleSheet()); - for (const QString& ts : completeBackups) { + + for (const QString& ts : completeBackups) + { QString dateStr = formatTimestamp(ts); QAction* action = backupSubmenu->addAction(QString("Backup from %1").arg(dateStr)); - connect(action, &QAction::triggered, [this, ts]() { + connect(action, &QAction::triggered, [this, ts]() + { restoreBackup(ts); }); } + menu.addMenu(backupSubmenu); - } else { + } + else + { QString ts = completeBackups.first(); QString dateStr = formatTimestamp(ts); + QAction* backupAction = menu.addAction( QString("Restore Backup (%1)").arg(dateStr)); - connect(backupAction, &QAction::triggered, [this, ts]() { + connect(backupAction, &QAction::triggered, [this, ts]() + { restoreBackup(ts); }); } - } else { + } + else + { QAction* backupAction = menu.addAction("No Backups Available"); backupAction->setEnabled(false); } + menu.exec(QCursor::pos()); } -void GeneralTab::restoreDefaultFiles() { +void GeneralTab::restoreDefaultFiles() +{ bool isKernelMode = settings->value("general/syscall_mode", "Nt").toString() == "Zw"; QString modeText = isKernelMode ? "kernel mode" : "user mode"; QString filePathText = isKernelMode ? "SysCallerK directory" : "SysCaller directory"; QString headerName = isKernelMode ? "sysFunctions_k.h" : "sysFunctions.h"; - ConfirmationDialog confirmDialog("Bind - v1.3.0", this); + + ConfirmationDialog confirmDialog("Bind - v1.3.1", this); confirmDialog.setMessage(QString("Are you sure you want to restore default %1 files?\n\n" "This will overwrite your current syscaller.asm and %2 files in the %3.") .arg(modeText, headerName, filePathText)); - if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) { + if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) + { return; } - try { + + try + { QString defaultAsmPath = PathUtils::getDefaultSysCallerAsmPath(); QString defaultHeaderPath = PathUtils::getDefaultSysFunctionsPath(isKernelMode); QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); - if (!QFile::exists(defaultAsmPath) || !QFile::exists(defaultHeaderPath)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (!QFile::exists(defaultAsmPath) || !QFile::exists(defaultHeaderPath)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("Default files not found in Default directory."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - if (createBackup->isChecked()) { + + if (createBackup->isChecked()) + { createBackupFiles(); } - if (QFile::exists(asmPath)) { + + if (QFile::exists(asmPath)) + { QFile::remove(asmPath); } - if (QFile::exists(headerPath)) { + + if (QFile::exists(headerPath)) + { QFile::remove(headerPath); } + bool asmCopied = QFile::copy(defaultAsmPath, asmPath); bool headerCopied = QFile::copy(defaultHeaderPath, headerPath); - if (!asmCopied || !headerCopied) { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + + if (!asmCopied || !headerCopied) + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage(QString("Failed to copy files:\nASM: %1\nHeader: %2") .arg(asmCopied ? "Success" : "Failed") .arg(headerCopied ? "Success" : "Failed")); @@ -306,235 +431,337 @@ void GeneralTab::restoreDefaultFiles() { errorDialog.exec(); return; } - ConfirmationDialog infoDialog("Bind - v1.3.0", this); + + ConfirmationDialog infoDialog("Bind - v1.3.1", this); infoDialog.setMessage(QString("Default %1 files have been restored successfully!").arg(modeText)); infoDialog.setButtons(false, false, true, false); infoDialog.exec(); - } catch (...) { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + } + catch (...) + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage("An error occurred while restoring default files."); errorDialog.setButtons(false, true, false); errorDialog.exec(); } } -void GeneralTab::restoreBackup(const QString& timestamp) { - try { +void GeneralTab::restoreBackup(const QString& timestamp) +{ + try + { QString backupsDir = PathUtils::getBackupsPath(); QStringList completeBackups = getAvailableBackups(); - if (!completeBackups.contains(timestamp)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (!completeBackups.contains(timestamp)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage(QString("Could not find complete backup set for timestamp %1").arg(timestamp)); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } + QString backupAsmPath = QString("%1/syscaller_%2.asm").arg(backupsDir, timestamp); QString backupHeaderPath = QString("%1/sysFunctions_%2.h").arg(backupsDir, timestamp); QStringList missingFiles; - if (!QFile::exists(backupAsmPath)) { + + if (!QFile::exists(backupAsmPath)) + { missingFiles << QString("ASM file: %1").arg(backupAsmPath); } - if (!QFile::exists(backupHeaderPath)) { + + if (!QFile::exists(backupHeaderPath)) + { missingFiles << QString("Header file: %1").arg(backupHeaderPath); } - if (!missingFiles.isEmpty()) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (!missingFiles.isEmpty()) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage(QString("Could not find the following backup files:\n%1").arg(missingFiles.join("\n"))); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - ConfirmationDialog confirmDialog("Bind - v1.3.0", this); + + ConfirmationDialog confirmDialog("Bind - v1.3.1", this); confirmDialog.setMessage(QString("Are you sure you want to restore from backup files dated %1?\n\n" "This will overwrite your current syscaller.asm and sysFunctions.h files.") .arg(formatTimestamp(timestamp))); - if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) { + if (confirmDialog.exec() != QDialog::Accepted || confirmDialog.getResult() != ConfirmationDialog::Yes) + { return; } + bool isKernelMode = settings->value("general/syscall_mode", "Nt").toString() == "Zw"; QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); + QDir().mkpath(QFileInfo(asmPath).absolutePath()); QDir().mkpath(QFileInfo(headerPath).absolutePath()); - if (QFile::exists(asmPath) && isFileLocked(asmPath)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (QFile::exists(asmPath) && isFileLocked(asmPath)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("The ASM file appears to be locked by another process. Close any applications that might be using it and try again."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - if (QFile::exists(headerPath) && isFileLocked(headerPath)) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + + if (QFile::exists(headerPath) && isFileLocked(headerPath)) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("The header file appears to be locked by another process. Close any applications that might be using it and try again."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); return; } - if (createBackup->isChecked()) { + + if (createBackup->isChecked()) + { createBackupFiles(); } + bool asmRestored = false; bool headerRestored = false; + asmRestored = restoreFileWithRetry(backupAsmPath, asmPath, "ASM"); - if (!asmRestored) { - if (QFile::exists(asmPath)) { + + if (!asmRestored) + { + if (QFile::exists(asmPath)) + { QFile::remove(asmPath); } + asmRestored = QFile::copy(backupAsmPath, asmPath); } + headerRestored = restoreFileWithRetry(backupHeaderPath, headerPath, "Header"); - if (!headerRestored) { - if (QFile::exists(headerPath)) { + + if (!headerRestored) + { + if (QFile::exists(headerPath)) + { QFile::remove(headerPath); } + headerRestored = QFile::copy(backupHeaderPath, headerPath); } - if (asmRestored && headerRestored) { - ConfirmationDialog infoDialog("Bind - v1.3.0", this); + + if (asmRestored && headerRestored) + { + ConfirmationDialog infoDialog("Bind - v1.3.1", this); infoDialog.setMessage(QString("Files have been restored from backup successfully!\n\nBackup date: %1") .arg(formatTimestamp(timestamp))); infoDialog.setButtons(false, false, true, false); infoDialog.exec(); - } else if (!asmRestored && headerRestored) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + } + else if (!asmRestored && headerRestored) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("Only the header file was restored successfully. The ASM file could not be restored."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); - } else if (asmRestored && !headerRestored) { - ConfirmationDialog warningDialog("Bind - v1.3.0", this); + } + else if (asmRestored && !headerRestored) + { + ConfirmationDialog warningDialog("Bind - v1.3.1", this); warningDialog.setMessage("Only the ASM file was restored successfully. The header file could not be restored."); warningDialog.setButtons(false, false, true, false); warningDialog.exec(); - } else { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + } + else + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage("Failed to restore both files from backup."); errorDialog.setButtons(false, false, true, false); errorDialog.exec(); } - } catch (...) { - ConfirmationDialog errorDialog("Bind - v1.3.0", this); + } + catch (...) + { + ConfirmationDialog errorDialog("Bind - v1.3.1", this); errorDialog.setMessage("An error occurred while restoring backup files."); errorDialog.setButtons(false, true, false); errorDialog.exec(); } } -void GeneralTab::openHashCompare() { +void GeneralTab::openHashCompare() +{ HashCompareDialog dialog(this); dialog.exec(); } -QString GeneralTab::formatTimestamp(const QString& timestamp) { - if (timestamp.contains('_')) { +QString GeneralTab::formatTimestamp(const QString& timestamp) +{ + if (timestamp.contains('_')) + { QStringList parts = timestamp.split('_'); - if (parts.size() >= 2) { + + if (parts.size() >= 2) + { QString datePart = parts[0]; QString timePart = parts[1]; - if (datePart.length() >= 8 && timePart.length() >= 4) { + + if (datePart.length() >= 8 && timePart.length() >= 4) + { QString year = datePart.mid(0, 4); QString month = datePart.mid(4, 2); QString day = datePart.mid(6, 2); QString hour = timePart.mid(0, 2); QString minute = timePart.mid(2, 2); + return QString("%1-%2-%3 %4:%5").arg(year, month, day, hour, minute); } } } + return timestamp; } -QString GeneralTab::getIniPath() { +QString GeneralTab::getIniPath() +{ return PathUtils::getIniPath(); } -bool GeneralTab::isFileLocked(const QString& filePath) { - if (!QFile::exists(filePath)) { +bool GeneralTab::isFileLocked(const QString& filePath) +{ + if (!QFile::exists(filePath)) + { return false; } + QFile file(filePath); - if (!file.open(QIODevice::ReadWrite)) { + + if (!file.open(QIODevice::ReadWrite)) + { return true; } + file.close(); return false; } -QStringList GeneralTab::getAvailableBackups() { +QStringList GeneralTab::getAvailableBackups() +{ QStringList completeBackups; QString backupsDir = PathUtils::getBackupsPath(); QDir dir(backupsDir); - if (!dir.exists()) { + + if (!dir.exists()) + { return completeBackups; } + QStringList filters; filters << "syscaller_*.asm"; + QFileInfoList asmFiles = dir.entryInfoList(filters, QDir::Files); - for (const QFileInfo& asmFile : asmFiles) { + + for (const QFileInfo& asmFile : asmFiles) + { QString fileName = asmFile.fileName(); - if (fileName.startsWith("syscaller_") && fileName.endsWith(".asm")) { + + if (fileName.startsWith("syscaller_") && fileName.endsWith(".asm")) + { QString timestamp = fileName.mid(10, fileName.length() - 14); QString headerFile = QString("sysFunctions_%1.h").arg(timestamp); QString headerPath = QString("%1/%2").arg(backupsDir, headerFile); - if (QFile::exists(headerPath)) { + + if (QFile::exists(headerPath)) + { completeBackups.append(timestamp); } } } + std::sort(completeBackups.begin(), completeBackups.end(), std::greater()); return completeBackups; } -void GeneralTab::createBackupFiles() { - try { +void GeneralTab::createBackupFiles() +{ + try + { QString backupsDir = PathUtils::getBackupsPath(); QDir().mkpath(backupsDir); + QDateTime now = QDateTime::currentDateTime(); QString timestamp = now.toString("yyyyMMdd_HHmmss"); + bool isKernelMode = settings->value("general/syscall_mode", "Nt").toString() == "Zw"; QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); + QString backupAsmPath = QString("%1/syscaller_%2.asm").arg(backupsDir, timestamp); QString backupHeaderPath = QString("%1/sysFunctions_%2.h").arg(backupsDir, timestamp); - if (QFile::exists(asmPath)) { + + if (QFile::exists(asmPath)) + { QFile::copy(asmPath, backupAsmPath); } - if (QFile::exists(headerPath)) { + + if (QFile::exists(headerPath)) + { QFile::copy(headerPath, backupHeaderPath); } - } catch (...) { + } + catch (...) + { // backup creation failed but dont stop the operation } } -bool GeneralTab::restoreFileWithRetry(const QString& sourcePath, const QString& destPath, const QString& fileType) { +bool GeneralTab::restoreFileWithRetry(const QString& sourcePath, const QString& destPath, const QString& fileType) +{ const int maxRetries = 3; int retryCount = 0; - while (retryCount < maxRetries) { - try { - if (QFile::exists(destPath)) { + + while (retryCount < maxRetries) + { + try + { + if (QFile::exists(destPath)) + { QFile::remove(destPath); } - if (QFile::copy(sourcePath, destPath)) { - if (QFile::exists(destPath)) { + + if (QFile::copy(sourcePath, destPath)) + { + if (QFile::exists(destPath)) + { QFileInfo destInfo(destPath); QFileInfo sourceInfo(sourcePath); - if (destInfo.size() > 0 && destInfo.size() == sourceInfo.size()) { + + if (destInfo.size() > 0 && destInfo.size() == sourceInfo.size()) + { return true; } } } + retryCount++; - if (retryCount < maxRetries) { + + if (retryCount < maxRetries) + { QThread::msleep(1000); } - } catch (...) { + } + catch (...) + { retryCount++; - if (retryCount < maxRetries) { + + if (retryCount < maxRetries) + { QThread::msleep(1000); } } } + return false; } diff --git a/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp b/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp index b600fe4..eaea4d2 100644 --- a/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/IndirectObfuscationTab.cpp @@ -3,32 +3,55 @@ #include #include -IndirectObfuscationTab::IndirectObfuscationTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +IndirectObfuscationTab::IndirectObfuscationTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void IndirectObfuscationTab::initUI() { +void IndirectObfuscationTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); setupJunkInstructionsGroup(); setupResolverObfuscationGroup(); setupEncryptionGroup(); setupControlFlowGroup(); - if (junkGroup) layout->addWidget(junkGroup); - if (resolverGroup) layout->addWidget(resolverGroup); - if (encryptionGroup) layout->addWidget(encryptionGroup); - if (controlFlowGroup) layout->addWidget(controlFlowGroup); + + if (junkGroup) + { + layout->addWidget(junkGroup); + } + + if (resolverGroup) + { + layout->addWidget(resolverGroup); + } + + if (encryptionGroup) + { + layout->addWidget(encryptionGroup); + } + + if (controlFlowGroup) + { + layout->addWidget(controlFlowGroup); + } + loadSettings(); } -void IndirectObfuscationTab::setupJunkInstructionsGroup() { +void IndirectObfuscationTab::setupJunkInstructionsGroup() +{ junkGroup = new QGroupBox("Indirect Junk Instructions"); QFormLayout* junkLayout = new QFormLayout(); + indirectMinInstructions = new QSpinBox(); indirectMinInstructions->setRange(1, 10); indirectMinInstructions->setValue(settings->value("obfuscation/indirect_min_instructions", 2).toInt()); indirectMinInstructions->setToolTip("Minimum number of junk instructions to add to indirect stubs"); junkLayout->addRow("Minimum Instructions:", indirectMinInstructions); + indirectMaxInstructions = new QSpinBox(); indirectMaxInstructions->setRange(1, 20); indirectMaxInstructions->setValue(settings->value("obfuscation/indirect_max_instructions", 8).toInt()); @@ -37,26 +60,34 @@ void IndirectObfuscationTab::setupJunkInstructionsGroup() { junkGroup->setLayout(junkLayout); } -void IndirectObfuscationTab::setupResolverObfuscationGroup() { +void IndirectObfuscationTab::setupResolverObfuscationGroup() +{ resolverGroup = new QGroupBox("Resolver Obfuscation"); QFormLayout* resolverLayout = new QFormLayout(); + indirectObfuscateCalls = new QCheckBox("Obfuscate Resolver Calls"); indirectObfuscateCalls->setChecked(settings->value("obfuscation/indirect_obfuscate_calls", true).toBool()); indirectObfuscateCalls->setToolTip("Obfuscate calls to the syscall resolver function using function pointers"); resolverLayout->addRow(indirectObfuscateCalls); + indirectResolverMethod = new QComboBox(); indirectResolverMethod->addItem("Random Pattern", "random"); indirectResolverMethod->addItem("Register Based (Safest)", "register"); indirectResolverMethod->addItem("Stack Based (Aligned)", "stack"); indirectResolverMethod->addItem("Indirect Data", "indirect"); indirectResolverMethod->addItem("Register Shuffle", "shuffle"); + QString savedMethod = settings->value("obfuscation/indirect_resolver_method", "random").toString(); int index = indirectResolverMethod->findData(savedMethod); - if (index >= 0) { + + if (index >= 0) + { indirectResolverMethod->setCurrentIndex(index); } + indirectResolverMethod->setToolTip("Choose the function pointer obfuscation method for resolver calls"); resolverLayout->addRow("Resolver Method:", indirectResolverMethod); + indirectEncryptStrings = new QCheckBox("Encrypt Nt* Resolver Names"); indirectEncryptStrings->setChecked(settings->value("obfuscation/indirect_encrypt_strings", false).toBool()); indirectEncryptStrings->setToolTip("Encrypt strings like NtOpenProcess and decrypt them on the stack at runtime before calling the resolver"); @@ -64,9 +95,11 @@ void IndirectObfuscationTab::setupResolverObfuscationGroup() { resolverGroup->setLayout(resolverLayout); } -void IndirectObfuscationTab::setupEncryptionGroup() { +void IndirectObfuscationTab::setupEncryptionGroup() +{ encryptionGroup = new QGroupBox("Encrypted Syscall Numbers"); QFormLayout* encryptionLayout = new QFormLayout(); + indirectEncryptSyscalls = new QCheckBox("Encrypt Syscall Numbers"); indirectEncryptSyscalls->setChecked(settings->value("obfuscation/indirect_encrypt_syscalls", false).toBool()); indirectEncryptSyscalls->setToolTip("Encrypt the syscall numbers in the generated shellcode"); @@ -74,30 +107,38 @@ void IndirectObfuscationTab::setupEncryptionGroup() { encryptionGroup->setLayout(encryptionLayout); } -void IndirectObfuscationTab::setupControlFlowGroup() { +void IndirectObfuscationTab::setupControlFlowGroup() +{ controlFlowGroup = new QGroupBox("Control Flow Obfuscation"); QFormLayout* controlFlowLayout = new QFormLayout(); + indirectEnableControlFlow = new QCheckBox("Enable Control Flow Obfuscation"); indirectEnableControlFlow->setChecked(settings->value("obfuscation/indirect_enable_control_flow", false).toBool()); indirectEnableControlFlow->setToolTip("Enable control flow obfuscation for indirect stubs"); controlFlowLayout->addRow(indirectEnableControlFlow); + indirectControlFlowMethod = new QComboBox(); indirectControlFlowMethod->addItem("Random Pattern", "random"); indirectControlFlowMethod->addItem("Register Based (Safest)", "register"); indirectControlFlowMethod->addItem("Value Based", "value"); indirectControlFlowMethod->addItem("Flag Based", "flag"); indirectControlFlowMethod->addItem("Mixed Junk Code", "mixed"); + QString savedMethod = settings->value("obfuscation/indirect_control_flow_method", "random").toString(); int index = indirectControlFlowMethod->findData(savedMethod); - if (index >= 0) { + + if (index >= 0) + { indirectControlFlowMethod->setCurrentIndex(index); } + indirectControlFlowMethod->setToolTip("Choose the control flow obfuscation method"); controlFlowLayout->addRow("Control Flow Method:", indirectControlFlowMethod); controlFlowGroup->setLayout(controlFlowLayout); } -void IndirectObfuscationTab::saveSettings() { +void IndirectObfuscationTab::saveSettings() +{ settings->setValue("obfuscation/indirect_min_instructions", indirectMinInstructions->value()); settings->setValue("obfuscation/indirect_max_instructions", indirectMaxInstructions->value()); settings->setValue("obfuscation/indirect_enable_junk", true); @@ -109,5 +150,6 @@ void IndirectObfuscationTab::saveSettings() { settings->setValue("obfuscation/indirect_control_flow_method", indirectControlFlowMethod->currentData().toString()); } -void IndirectObfuscationTab::loadSettings() { -} +void IndirectObfuscationTab::loadSettings() +{ +} diff --git a/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp b/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp index 571c886..00d1f14 100644 --- a/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/InlineObfuscationTab.cpp @@ -2,19 +2,24 @@ #include #include -InlineObfuscationTab::InlineObfuscationTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +InlineObfuscationTab::InlineObfuscationTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void InlineObfuscationTab::initUI() { +void InlineObfuscationTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); - + QLabel* comingSoonLabel = new QLabel("Inline Assembly Obfuscation is coming soon!"); comingSoonLabel->setAlignment(Qt::AlignCenter); comingSoonLabel->setStyleSheet("font-size: 16px; color: #888888; margin: 50px;"); - + layout->addWidget(comingSoonLabel); } -void InlineObfuscationTab::saveSettings() {} +void InlineObfuscationTab::saveSettings() +{ +} diff --git a/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp b/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp index 624fd4b..fabcf06 100644 --- a/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/IntegrityTab.cpp @@ -7,84 +7,119 @@ #include #include -IntegrityTab::IntegrityTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +IntegrityTab::IntegrityTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void IntegrityTab::initUI() { +void IntegrityTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); + QGroupBox* syscallGroup = new QGroupBox("Syscall Selection"); syscallGroup->setToolTip("Select which syscalls to include in the final build"); + QVBoxLayout* syscallLayout = new QVBoxLayout(); + QLabel* desc = new QLabel("Select the syscalls to include in the final build. Only selected syscalls will be processed during integrity checks and included in the final build!"); desc->setWordWrap(true); syscallLayout->addWidget(desc); + QHBoxLayout* selectLayout = new QHBoxLayout(); + QPushButton* selectAllBtn = new QPushButton("Select All"); connect(selectAllBtn, &QPushButton::clicked, this, &IntegrityTab::selectAllSyscalls); selectLayout->addWidget(selectAllBtn); + QPushButton* selectNoneBtn = new QPushButton("Select None"); connect(selectNoneBtn, &QPushButton::clicked, this, &IntegrityTab::selectNoSyscalls); selectLayout->addWidget(selectNoneBtn); + filterEdit = new QLineEdit(); filterEdit->setPlaceholderText("Filter syscalls..."); connect(filterEdit, &QLineEdit::textChanged, this, &IntegrityTab::filterSyscalls); selectLayout->addWidget(filterEdit, 1); syscallLayout->addLayout(selectLayout); + syscallList = new QListWidget(); syscallList->setSelectionMode(QAbstractItemView::NoSelection); syscallLayout->addWidget(syscallList); syscallGroup->setLayout(syscallLayout); layout->addWidget(syscallGroup); + loadSyscalls(); } -void IntegrityTab::loadSyscalls() { +void IntegrityTab::loadSyscalls() +{ syscalls.clear(); PathUtils::debugPathDetection(); + QString projectRoot = PathUtils::getProjectRoot(); qDebug() << "Project root:" << projectRoot; + QString syscallMode = settings->value("general/syscall_mode", "Nt").toString(); bool isKernelMode = (syscallMode == "Zw"); QString syscallPrefix = (syscallMode == "Nt") ? "Sys" : "SysK"; QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); + qDebug() << "Header path:" << headerPath; qDebug() << "Header file exists:" << QFile::exists(headerPath); + QStringList selectedSyscalls = settings->value("integrity/selected_syscalls", QStringList()).toStringList(); + QFile file(headerPath); - if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) { + + if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) + { qDebug() << "Successfully opened header file:" << headerPath; + QTextStream stream(&file); stream.setCodec("UTF-8"); int lineCount = 0; - while (!stream.atEnd()) { + + while (!stream.atEnd()) + { QString line = stream.readLine(); lineCount++; + QRegExp externCRegex(QString(R"(extern "C" (?:NTSTATUS|ULONG|BOOLEAN|VOID) (%1\w+)\()").arg(syscallPrefix)); - if (externCRegex.indexIn(line) != -1) { + + if (externCRegex.indexIn(line) != -1) + { QString syscall = externCRegex.cap(1); qDebug() << "Found extern C syscall:" << syscall; syscalls.append(syscall); continue; } + QRegExp regularRegex(QString(R"((?:NTSTATUS|ULONG|BOOLEAN|VOID) (%1\w+)\()").arg(syscallPrefix)); - if (regularRegex.indexIn(line) != -1) { + + if (regularRegex.indexIn(line) != -1) + { QString syscall = regularRegex.cap(1); qDebug() << "Found regular syscall:" << syscall; syscalls.append(syscall); continue; } + QRegExp scExternCRegex(R"(extern "C" (?:NTSTATUS|ULONG|BOOLEAN|VOID) (SC\w+)\()"); - if (scExternCRegex.indexIn(line) != -1) { + + if (scExternCRegex.indexIn(line) != -1) + { QString scName = scExternCRegex.cap(1); QString syscallName = syscallPrefix + scName.mid(2); qDebug() << "Found SC extern C syscall:" << scName << "->" << syscallName; syscalls.append(syscallName); continue; } + QRegExp scRegularRegex(R"((?:NTSTATUS|ULONG|BOOLEAN|VOID) (SC\w+)\()"); - if (scRegularRegex.indexIn(line) != -1) { + + if (scRegularRegex.indexIn(line) != -1) + { QString scName = scRegularRegex.cap(1); QString syscallName = syscallPrefix + scName.mid(2); qDebug() << "Found SC regular syscall:" << scName << "->" << syscallName; @@ -92,69 +127,104 @@ void IntegrityTab::loadSyscalls() { continue; } } + qDebug() << "Processed" << lineCount << "lines from header file"; file.close(); - } else { + } + else + { qWarning() << "Failed to open header file:" << headerPath; qWarning() << "File exists:" << file.exists(); qWarning() << "File error:" << file.errorString(); } + syscalls.sort(); qDebug() << "Found" << syscalls.size() << "syscalls"; - for (const QString& syscall : syscalls) { + + for (const QString& syscall : syscalls) + { qDebug() << " " << syscall; } - if (selectedSyscalls.isEmpty()) { + + if (selectedSyscalls.isEmpty()) + { selectedSyscalls = syscalls; } - for (const QString& syscall : syscalls) { + + for (const QString& syscall : syscalls) + { QListWidgetItem* item = new QListWidgetItem(syscall); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - if (selectedSyscalls.contains(syscall)) { + + if (selectedSyscalls.contains(syscall)) + { item->setCheckState(Qt::Checked); - } else { + } + else + { item->setCheckState(Qt::Unchecked); } + syscallList->addItem(item); } } -void IntegrityTab::selectAllSyscalls() { - for (int i = 0; i < syscallList->count(); ++i) { +void IntegrityTab::selectAllSyscalls() +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (!item->isHidden()) { + + if (!item->isHidden()) + { item->setCheckState(Qt::Checked); } } } -void IntegrityTab::selectNoSyscalls() { - for (int i = 0; i < syscallList->count(); ++i) { +void IntegrityTab::selectNoSyscalls() +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (!item->isHidden()) { + + if (!item->isHidden()) + { item->setCheckState(Qt::Unchecked); } } } -void IntegrityTab::filterSyscalls(const QString& text) { - for (int i = 0; i < syscallList->count(); ++i) { +void IntegrityTab::filterSyscalls(const QString& text) +{ + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (text.isEmpty() || item->text().toLower().contains(text.toLower())) { + + if (text.isEmpty() || item->text().toLower().contains(text.toLower())) + { item->setHidden(false); - } else { + } + else + { item->setHidden(true); } } } -void IntegrityTab::saveSettings() { +void IntegrityTab::saveSettings() +{ QStringList selectedSyscalls; - for (int i = 0; i < syscallList->count(); ++i) { + + for (int i = 0; i < syscallList->count(); ++i) + { QListWidgetItem* item = syscallList->item(i); - if (item->checkState() == Qt::Checked) { + + if (item->checkState() == Qt::Checked) + { selectedSyscalls.append(item->text()); } } + settings->setValue("integrity/selected_syscalls", selectedSyscalls); -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp b/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp index f94ab70..b34f2ba 100644 --- a/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/ObfuscationTab.cpp @@ -1,44 +1,57 @@ #include "include/GUI/Settings/Tabs/ObfuscationTab.h" +#include "include/Core/Obfuscation/Direct/Encryption/DirectEncryptor.h" #include -ObfuscationTab::ObfuscationTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +ObfuscationTab::ObfuscationTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void ObfuscationTab::initUI() { +void ObfuscationTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); + QGroupBox* junkGroup = new QGroupBox("Junk Instructions"); QFormLayout* junkLayout = new QFormLayout(); + minInstructions = new QSpinBox(); minInstructions->setRange(1, 10); minInstructions->setValue(settings->value("obfuscation/min_instructions", 2).toInt()); junkLayout->addRow("Minimum Instructions:", minInstructions); + maxInstructions = new QSpinBox(); maxInstructions->setRange(1, 20); maxInstructions->setValue(settings->value("obfuscation/max_instructions", 8).toInt()); junkLayout->addRow("Maximum Instructions:", maxInstructions); + useAdvancedJunk = new QCheckBox("Advanced Junk Instructions"); useAdvancedJunk->setChecked(settings->value("obfuscation/use_advanced_junk", false).toBool()); junkLayout->addRow(useAdvancedJunk); junkGroup->setLayout(junkLayout); layout->addWidget(junkGroup); + QGroupBox* nameGroup = new QGroupBox("Name Randomization"); QFormLayout* nameLayout = new QFormLayout(); + QHBoxLayout* syscallPrefixLayout = new QHBoxLayout(); syscallPrefixLength = new QSpinBox(); syscallPrefixLength->setRange(4, 16); syscallPrefixLength->setValue(settings->value("obfuscation/syscall_prefix_length", 8).toInt()); syscallPrefixLength->setToolTip("Length of the syscall prefix in the syscall stub"); + syscallNumberLength = new QSpinBox(); syscallNumberLength->setRange(4, 16); syscallNumberLength->setValue(settings->value("obfuscation/syscall_number_length", 6).toInt()); syscallNumberLength->setToolTip("Length of the syscall number in the syscall stub"); + syscallPrefixLayout->addWidget(new QLabel("Chars:")); syscallPrefixLayout->addWidget(syscallPrefixLength); syscallPrefixLayout->addWidget(new QLabel("Numbers:")); syscallPrefixLayout->addWidget(syscallNumberLength); nameLayout->addRow("Syscall Name Length:", syscallPrefixLayout); + offsetNameLength = new QSpinBox(); offsetNameLength->setRange(4, 16); offsetNameLength->setValue(settings->value("obfuscation/offset_name_length", 8).toInt()); @@ -46,83 +59,105 @@ void ObfuscationTab::initUI() { nameLayout->addRow("Offset Name Length:", offsetNameLength); nameGroup->setLayout(nameLayout); layout->addWidget(nameGroup); + QGroupBox* sequenceGroup = new QGroupBox("Sequence Shuffling"); QFormLayout* sequenceLayout = new QFormLayout(); + shuffleSequence = new QCheckBox("Enable Sequence Shuffling"); shuffleSequence->setChecked(settings->value("obfuscation/shuffle_sequence", true).toBool()); shuffleSequence->setToolTip("Randomize the order of syscall stubs in the assembly file"); sequenceLayout->addRow(shuffleSequence); sequenceGroup->setLayout(sequenceLayout); layout->addWidget(sequenceGroup); + QGroupBox* encryptionGroup = new QGroupBox("Syscall Encryption"); QFormLayout* encryptionLayout = new QFormLayout(); + enableEncryption = new QCheckBox("Enable Syscall ID Encryption"); enableEncryption->setChecked(settings->value("obfuscation/enable_encryption", true).toBool()); enableEncryption->setToolTip("Encrypt syscall IDs in the data section to make static analysis harder"); encryptionLayout->addRow(enableEncryption); + encryptionMethod = new QComboBox(); - encryptionMethod->addItem("Basic XOR (Simple)", 1); - encryptionMethod->addItem("Multi-key XOR (Medium)", 2); - encryptionMethod->addItem("Add + XOR (Medium)", 3); - encryptionMethod->addItem("Enhanced XOR (Medium)", 4); - encryptionMethod->addItem("Offset Shifting (Medium)", 5); - int currentMethod = settings->value("obfuscation/encryption_method", 1).toInt(); + encryptionMethod->addItem("Basic XOR (Simple)", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)); + encryptionMethod->addItem("Multi key XOR (Medium)", static_cast(DirectObfuscation::EncryptionMethod::MultiKeyXOR)); + encryptionMethod->addItem("Add + XOR (Medium)", static_cast(DirectObfuscation::EncryptionMethod::AddXORCombo)); + encryptionMethod->addItem("Enhanced XOR (Medium)", static_cast(DirectObfuscation::EncryptionMethod::EnhancedXOR)); + encryptionMethod->addItem("Offset Shifting (Medium)", static_cast(DirectObfuscation::EncryptionMethod::OffsetShifting)); + + int currentMethod = settings->value("obfuscation/encryption_method", static_cast(DirectObfuscation::EncryptionMethod::BasicXOR)).toInt(); int index = encryptionMethod->findData(currentMethod); - if (index >= 0) { + + if (index >= 0) + { encryptionMethod->setCurrentIndex(index); } + encryptionMethod->setToolTip("Select the encryption method to use for syscall ID obfuscation"); encryptionLayout->addRow("Encryption Method:", encryptionMethod); encryptionGroup->setLayout(encryptionLayout); layout->addWidget(encryptionGroup); + QGroupBox* chunkingGroup = new QGroupBox("Function Chunking"); QFormLayout* chunkingLayout = new QFormLayout(); + enableChunking = new QCheckBox("Enable Function Chunking"); enableChunking->setChecked(settings->value("obfuscation/enable_chunking", true).toBool()); enableChunking->setToolTip("Split syscall stubs into multiple fragments to make analysis harder"); chunkingLayout->addRow(enableChunking); chunkingGroup->setLayout(chunkingLayout); layout->addWidget(chunkingGroup); + QGroupBox* interleavedGroup = new QGroupBox("Interleaved Execution"); QFormLayout* interleavedLayout = new QFormLayout(); + enableInterleaved = new QCheckBox("Enable Interleaved Execution"); enableInterleaved->setChecked(settings->value("obfuscation/enable_interleaved", true).toBool()); enableInterleaved->setToolTip("Mix code from different syscalls using ALIGN directives and random padding"); interleavedLayout->addRow(enableInterleaved); interleavedGroup->setLayout(interleavedLayout); layout->addWidget(interleavedGroup); + QGroupBox* controlFlowGroup = new QGroupBox("Control Flow"); QFormLayout* controlFlowLayout = new QFormLayout(); + enableControlFlow = new QCheckBox("Enable Control Flow"); enableControlFlow->setChecked(settings->value("obfuscation/control_flow_enabled", false).toBool()); enableControlFlow->setToolTip("Enable control flow obfuscation techniques"); - controlFlowLayout->addRow(enableControlFlow); + controlFlowLayout->addRow(enableControlFlow); + opaquePredicates = new QCheckBox("Opaque Predicates"); opaquePredicates->setChecked(settings->value("obfuscation/control_flow_opaque_predicates", false).toBool()); opaquePredicates->setToolTip("Add conditional statements that always evaluate to true/false"); - controlFlowLayout->addRow(opaquePredicates); + controlFlowLayout->addRow(opaquePredicates); + bogusControlFlow = new QCheckBox("Bogus Control Flow"); bogusControlFlow->setChecked(settings->value("obfuscation/control_flow_bogus_flow", false).toBool()); bogusControlFlow->setToolTip("Add fake conditional branches that never execute"); - controlFlowLayout->addRow(bogusControlFlow); + controlFlowLayout->addRow(bogusControlFlow); + indirectJumps = new QCheckBox("Indirect Jumps"); indirectJumps->setChecked(settings->value("obfuscation/control_flow_indirect_jumps", false).toBool()); indirectJumps->setToolTip("Use indirect addressing for jump instructions"); - controlFlowLayout->addRow(indirectJumps); + controlFlowLayout->addRow(indirectJumps); + conditionalBranches = new QCheckBox("Conditional Branches"); conditionalBranches->setChecked(settings->value("obfuscation/control_flow_conditional_branches", false).toBool()); conditionalBranches->setToolTip("Add conditional branches"); - controlFlowLayout->addRow(conditionalBranches); + controlFlowLayout->addRow(conditionalBranches); + controlFlowComplexity = new QSpinBox(); controlFlowComplexity->setRange(1, 10); controlFlowComplexity->setValue(settings->value("obfuscation/control_flow_complexity", 2).toInt()); controlFlowComplexity->setToolTip("Number of control flow elements to add"); - controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + controlFlowLayout->addRow("Complexity Level:", controlFlowComplexity); + controlFlowGroup->setLayout(controlFlowLayout); layout->addWidget(controlFlowGroup); } -void ObfuscationTab::saveSettings() { +void ObfuscationTab::saveSettings() +{ settings->setValue("obfuscation/min_instructions", minInstructions->value()); settings->setValue("obfuscation/max_instructions", maxInstructions->value()); settings->setValue("obfuscation/use_advanced_junk", useAdvancedJunk->isChecked()); @@ -140,4 +175,4 @@ void ObfuscationTab::saveSettings() { settings->setValue("obfuscation/control_flow_indirect_jumps", indirectJumps->isChecked()); settings->setValue("obfuscation/control_flow_conditional_branches", conditionalBranches->isChecked()); settings->setValue("obfuscation/control_flow_complexity", controlFlowComplexity->value()); -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp b/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp index 7b3e875..2f832e8 100644 --- a/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp +++ b/Bind/src/GUI/Settings/Tabs/ProfileTab.cpp @@ -3,14 +3,18 @@ #include #include -ProfileTab::ProfileTab(QSettings* settings, QWidget* parent) - : QWidget(parent), settings(settings) { +ProfileTab::ProfileTab(QSettings* settings, QWidget* parent) + : QWidget(parent) + , settings(settings) +{ initUI(); } -void ProfileTab::initUI() { +void ProfileTab::initUI() +{ QVBoxLayout* layout = new QVBoxLayout(this); layout->addSpacing(20); + QPushButton* exportBtn = new QPushButton("Export Profile (.ini)"); exportBtn->setMinimumHeight(40); exportBtn->setStyleSheet( @@ -27,9 +31,12 @@ void ProfileTab::initUI() { " border: 1px solid rgba(255, 255, 255, 0.3);" "}" ); + connect(exportBtn, &QPushButton::clicked, this, &ProfileTab::exportProfile); layout->addWidget(exportBtn); + layout->addSpacing(10); + QPushButton* importBtn = new QPushButton("Import Profile (.ini)"); importBtn->setMinimumHeight(40); importBtn->setStyleSheet( @@ -46,61 +53,92 @@ void ProfileTab::initUI() { " border: 1px solid rgba(255, 255, 255, 0.3);" "}" ); + connect(importBtn, &QPushButton::clicked, this, &ProfileTab::importProfile); layout->addWidget(importBtn); + layout->addStretch(); } -void ProfileTab::exportProfile() { +void ProfileTab::exportProfile() +{ QString path = QFileDialog::getSaveFileName(this, "Export Profile", "", "INI Files (*.ini);;All Files (*)"); - if (path.isEmpty()) { + + if (path.isEmpty()) + { return; } - if (!path.toLower().endsWith(".ini")) { + + if (!path.toLower().endsWith(".ini")) + { path += ".ini"; } - try { + + try + { QSettings exportSettings(path, QSettings::IniFormat); exportSettings.clear(); + settings->sync(); - for (const QString& group : settings->childGroups()) { + + for (const QString& group : settings->childGroups()) + { settings->beginGroup(group); exportSettings.beginGroup(group); - for (const QString& key : settings->childKeys()) { + + for (const QString& key : settings->childKeys()) + { exportSettings.setValue(key, settings->value(key)); } + exportSettings.endGroup(); settings->endGroup(); } + exportSettings.sync(); - QMessageBox::information(this, "Bind - v1.3.0", - QString("Profile exported to:\n%1").arg(QDir::toNativeSeparators(QFileInfo(path).absoluteFilePath()))); - } catch (...) { - QMessageBox::critical(this, "Bind - v1.3.0", "Failed to export profile."); + + QMessageBox::information(this, "Bind - v1.3.1", + QString("Profile exported to:\n%1") + .arg(QDir::toNativeSeparators(QFileInfo(path).absoluteFilePath()))); + } + catch (...) + { + QMessageBox::critical(this, "Bind - v1.3.1", "Failed to export profile."); } } -void ProfileTab::importProfile() { - QString path = QFileDialog::getOpenFileName(this, "Bind - v1.3.0", "", "INI Files (*.ini);;All Files (*)"); - if (path.isEmpty()) { +void ProfileTab::importProfile() +{ + QString path = QFileDialog::getOpenFileName(this, "Bind - v1.3.1", "", "INI Files (*.ini);;All Files (*)"); + + if (path.isEmpty()) + { return; } - try { + + try + { QString iniPath = PathUtils::getIniPath(); settings->sync(); delete settings; settings = nullptr; + QFile::remove(iniPath); QFile::copy(path, iniPath); - QMessageBox::information(this, "Bind - v1.3.0", + + QMessageBox::information(this, "Bind - v1.3.1", QString("Profile imported from:\n%1\n\nSysCaller will now restart to use the imported profile.") .arg(QDir::toNativeSeparators(QFileInfo(path).absoluteFilePath()))); + QProcess::startDetached(QApplication::applicationFilePath(), QApplication::arguments()); QApplication::quit(); - } catch (...) { - QMessageBox::critical(this, "Bind - v1.3.0", "Failed to import profile."); + } + catch (...) + { + QMessageBox::critical(this, "Bind - v1.3.1", "Failed to import profile."); } } -void ProfileTab::saveSettings() { -} \ No newline at end of file +void ProfileTab::saveSettings() +{ +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/CompatibilityThread.cpp b/Bind/src/GUI/Threads/CompatibilityThread.cpp index 45becf2..eaaf17d 100644 --- a/Bind/src/GUI/Threads/CompatibilityThread.cpp +++ b/Bind/src/GUI/Threads/CompatibilityThread.cpp @@ -3,40 +3,58 @@ #include #include -CompatibilityThread::CompatibilityThread(QObject* parent) : QThread(parent) { -} +CompatibilityThread::CompatibilityThread(QObject* parent) + : QThread(parent) +{} -void CompatibilityThread::setDllPaths(const QStringList& paths) { +void CompatibilityThread::setDllPaths(const QStringList& paths) +{ dllPaths = paths; } -void CompatibilityThread::run() { +void CompatibilityThread::run() +{ qDebug() << "CompatibilityThread::run() called"; qDebug() << "DLL paths in thread:" << dllPaths; + emit compatibilityStarted(); emit progressUpdated("Starting Compatibility Check..."); - try { + + try + { qDebug() << "Creating Compatibility instance..."; + Compatibility compatibility; - compatibility.setOutputCallback([this](const QString& message) { + compatibility.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); + emit progressUpdated("Processing NTDLL..."); qDebug() << "Calling compatibility.runWithDllPaths()..."; + int result = compatibility.runWithDllPaths(dllPaths); - if (result == 0) { + + if (result == 0) + { emit progressUpdated("Compatibility Check Completed Successfully"); emit compatibilityFinished(true, "Compatibility Check Completed Successfully!"); - } else { + } + else + { emit progressUpdated("Compatibility Check Failed"); emit compatibilityFinished(false, "Compatibility Check failed with Error Code: " + QString::number(result)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { QString errorMsg = QString("Compatibility Error: %1").arg(e.what()); emit progressUpdated("Compatibility Error Occurred"); emit compatibilityFinished(false, errorMsg); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown Compatibility Error Occurred"); emit compatibilityFinished(false, "Unknown Error occurred during Compatibility Check"); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/ObfuscationThread.cpp b/Bind/src/GUI/Threads/ObfuscationThread.cpp index 4dd6d74..1b2b0b2 100644 --- a/Bind/src/GUI/Threads/ObfuscationThread.cpp +++ b/Bind/src/GUI/Threads/ObfuscationThread.cpp @@ -7,64 +7,96 @@ #include #include -ObfuscationThread::ObfuscationThread(QObject* parent) : QThread(parent) { -} +ObfuscationThread::ObfuscationThread(QObject* parent) + : QThread(parent) +{} -void ObfuscationThread::setOutputCallback(std::function callback) { +void ObfuscationThread::setOutputCallback(std::function callback) +{ outputCallback = callback; } -void ObfuscationThread::run() { +void ObfuscationThread::run() +{ qDebug() << "ObfuscationThread::run() called"; emit obfuscationStarted(); emit progressUpdated("Starting Syscall Obfuscation..."); - try { + + try + { qDebug() << "Creating Obfuscation Instance..."; + Obfuscation obfuscation; - obfuscation.setOutputCallback([this](const QString& message) { + obfuscation.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); + emit progressUpdated("Processing Syscall List For Obfuscation..."); qDebug() << "Calling obfuscation.run()..."; + int result = obfuscation.run(); - if (result == 0) { + + if (result == 0) + { emit progressUpdated("Stubs & Exports Generated Successfully!"); + QSettings settings(PathUtils::getIniPath(), QSettings::IniFormat); bool hashStubsEnabled = settings.value("general/hash_stubs", false).toBool(); - if (hashStubsEnabled) { + + if (hashStubsEnabled) + { QString lastMethod = settings.value("obfuscation/last_method", "").toString(); QString obfuscationType = (lastMethod == "stub_mapper") ? "Stub Mapper" : "Normal"; + emit progressUpdated(QString("Generating Stub Hashes for %1 Obfuscation...").arg(obfuscationType)); - try { + + try + { bool isKernelMode = settings.value("general/syscall_mode", "Nt").toString() == "Zw"; QString asmPath = PathUtils::getSysCallerAsmPath(isKernelMode); QString headerPath = PathUtils::getSysFunctionsPath(isKernelMode); QVariantMap stubHashes = StubHashGenerator::generateStubHashes(asmPath, headerPath, lastMethod); QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"); QPair saveResult = StubHashGenerator::saveStubHashes(stubHashes, timestamp); - if (saveResult.first) { + + if (saveResult.first) + { QString fileName = QFileInfo(saveResult.second).fileName(); emit progressUpdated(QString("Stub Hashes Saved to: %1").arg(fileName)); - } else { + } + else + { emit progressUpdated(QString("Failed to save Stub Hashes: %1").arg(saveResult.second)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { emit progressUpdated(QString("Error Generating Stub Hashes: %1").arg(e.what())); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown error occurred while generating Stub Hashes"); } } + emit obfuscationFinished(true, "Obfuscation Completed Successfully!"); - } else { + } + else + { emit progressUpdated("Obfuscation Failed With Error Code: " + QString::number(result)); emit obfuscationFinished(false, "Obfuscation failed with Error Code: " + QString::number(result)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { QString errorMsg = QString("Obfuscation Error: %1").arg(e.what()); emit progressUpdated("Obfuscation Error Occurred"); emit obfuscationFinished(false, errorMsg); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown Obfuscation Error Occurred"); emit obfuscationFinished(false, "Unknown Error occurred during Obfuscation"); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/ValidatorThread.cpp b/Bind/src/GUI/Threads/ValidatorThread.cpp index 27e5825..68b425e 100644 --- a/Bind/src/GUI/Threads/ValidatorThread.cpp +++ b/Bind/src/GUI/Threads/ValidatorThread.cpp @@ -3,42 +3,58 @@ #include #include -ValidatorThread::ValidatorThread(QObject* parent) : QThread(parent) { -} +ValidatorThread::ValidatorThread(QObject* parent) + : QThread(parent) +{} -void ValidatorThread::setDllPaths(const QStringList& paths) { +void ValidatorThread::setDllPaths(const QStringList& paths) +{ dllPaths = paths; } -void ValidatorThread::run() { +void ValidatorThread::run() +{ qDebug() << "ValidatorThread::run() called"; qDebug() << "DLL paths in thread:" << dllPaths; + emit validationStarted(); emit progressUpdated("Starting Validation Check..."); - try { + + try + { qDebug() << "Creating Validator Instance..."; + Validator validator; - validator.setOutputCallback([this](const QString& message) { + validator.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); + emit progressUpdated("Processing NTDLL..."); qDebug() << "Calling validator.runWithDllPaths()..."; + int result = validator.runWithDllPaths(dllPaths); - if (result == 0) { + + if (result == 0) + { emit progressUpdated("Validation Check Completed Successfully"); emit validationFinished(true, "Validation Completed Successfully!"); - } else { + } + else + { emit progressUpdated("Validation Check Failed"); emit validationFinished(false, "Validation failed with Error Code: " + QString::number(result)); } - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { QString errorMsg = QString("Validation Error: %1").arg(e.what()); emit progressUpdated("Validation Error Occurred"); emit validationFinished(false, errorMsg); - } catch (...) { + } + catch (...) + { emit progressUpdated("Unknown Validation Error Occurred"); emit validationFinished(false, "Unknown Error occurred during Validation"); } -} - - \ No newline at end of file +} \ No newline at end of file diff --git a/Bind/src/GUI/Threads/VerificationThread.cpp b/Bind/src/GUI/Threads/VerificationThread.cpp index 9b8ca62..27cd127 100644 --- a/Bind/src/GUI/Threads/VerificationThread.cpp +++ b/Bind/src/GUI/Threads/VerificationThread.cpp @@ -1,26 +1,35 @@ #include "include/GUI/Threads/VerificationThread.h" #include -VerificationThread::VerificationThread(QObject* parent) : QThread(parent) { -} +VerificationThread::VerificationThread(QObject* parent) + : QThread(parent) +{} -void VerificationThread::setDllPaths(const QStringList& paths) { +void VerificationThread::setDllPaths(const QStringList& paths) +{ dllPaths = paths; } -void VerificationThread::setOutputCallback(std::function callback) { +void VerificationThread::setOutputCallback(std::function callback) +{ outputCallback = callback; - verification.setOutputCallback([this](const QString& message) { + verification.setOutputCallback([this](const QString& message) + { emit progressUpdated(message); }); } -void VerificationThread::run() { +void VerificationThread::run() +{ emit verificationStarted(); - try { + + try + { int result = verification.runWithDllPaths(dllPaths); emit verificationFinished(result == 0, result == 0 ? "Verification Completed Successfully" : "Verification Failed"); - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { qDebug() << "Verification Thread Exception:" << e.what(); emit verificationFinished(false, QString("Verification Failed: %1").arg(e.what())); } diff --git a/Bind/src/Res/Icons/compatibility.png b/Bind/src/Res/Icons/compatibility.png new file mode 100644 index 0000000000000000000000000000000000000000..16d2ef5891b4d2ab8f4614e0bdcf977aac286847 GIT binary patch literal 921 zcmV;K17`e*P)rNnZ@F_+X6sB0^Nk3yCE$F~kIT<1b)CHgqR8 zwGU8eOSg3v)@9kQ)7hQBdp&o$t#|Ux+;i_aXU_NCbER!xPeM@?v?P;|B2WQ-mErNUQctAIqswpn)vCH zy~hu}+OswN#)j87Ps~lGC~kB$fy@~@?|EL{wygYLH!BNGLFzc{ANZ2jOAncsZ$&WG9G?p@Ys ze#)o7XvR4dC;fhZZfqKl@{fvBe@qmE(P%o>mBf{cBj$^)OWcV(*_CSRig(01+RIC2 zqhU4nP=a9!&l0YwMB$|1&pZz@Q8D{A;Qp@{kjXxdcE1+sNk3=G1 zjNxkTnqR4EbX=xN7;HTaX?dge;KElgZy72@Lp_gCsWgQmk*)f2b$skbLD;WsAIQMv z{+@&%65_pLR5yXQ&*t~-9Lm;ewKYA$;V}Np-I-c8YG=t5It(Ki&=6EdR5FH$(-*># zCuY6_2}!2{+p%GqCd|5tP%s1vw#a3qne!w$KtRF%(3$;%2M#7$+FpA4RNs+oT^Dzl z+g^H6f@zynDfNfKVHrk+xvtkta2kr5Mj)U`_%@y&-1Tl&{7ndCS|;WybC4w&s-pVd zoF3PxSC};x3EH2Rcm#pFKNo-k{u3VX)c5C)I!IxQ{ z=RB)#4h^)X5Tq4?wP-%$Hv3t{Ek|68mCMm913ePl2=SKuW73+OHP9Af|92`H)QS&X zTE1 \ No newline at end of file diff --git a/Bind/src/Res/Icons/export.png b/Bind/src/Res/Icons/export.png new file mode 100644 index 0000000000000000000000000000000000000000..f78bfb28860d7002c7f934eea7026f84ddaa9c40 GIT binary patch literal 628 zcmV-)0*n2LP)Q2n3Lvhd%vJrBZ>iZNu65In>cVDV5;M z#>T5uKEH@;ap^_?BmyWQLf?qT!R+scheJc4D+M9~{(W0|W4Mc^Rs%Ybz-~w{T)5(BSBLXUH66!bhHo%8-#RwG8d~>1mKs z!qUo$-==xFxIh5 \ No newline at end of file diff --git a/Bind/src/Res/Icons/obfuscation.png b/Bind/src/Res/Icons/obfuscation.png new file mode 100644 index 0000000000000000000000000000000000000000..09f73c230c370b2743eb85fb6fe37959003549ff GIT binary patch literal 614 zcmV-s0-61ZP)uo$9za0n!RK6IEMzcnv zalTwGU*lN5Xl%0x@a7@J@ArGLm-qX!KR`P9K+a07Q|7mej3=QxOXz21|}W<$(& zyEQ<76V{nFm&$id(-!G;T1=)7V15*`GD!Mkp^TWPo31V`HKb`*=n zsMTuGa5&V#7ubEitc0iadc7)1lHTw4sn_e#Y&N6ucubSYL}wk_T-5`XVyDxY@Fo(8 zP%@dMQmI6>T8;0D$HrCx&br<1LQxdT=kvs+AZ^ZOar^=-*DTI254|ZA3WRb9uXyO! zEAS5JRxhA|PvAV4%ZW@TBQQXsQmGjGY&I)cXWD4TukZxZR}6(h_Y;XkK-YDWWtpPU zC^wIq&89IE;c)mS1}n5)uODMJrttfA808;ujKWE_+wDfmyw2xyL+SB&i~y_E$`~p< z?Y>&A{(|Zr*X#nPPN$Ro$>DGq0eDqam4pz)8QE+$qaSR8J8u3d`%31Y_X0cvf55_I z2{vYi4)a4E;0D~)kKjEAP{1v7{U6za-+u)d05p2oGJV38LI3~&07*qoM6N<$g3})! AAOHXW literal 0 HcmV?d00001 diff --git a/Bind/src/Res/Icons/obfuscation.svg b/Bind/src/Res/Icons/obfuscation.svg deleted file mode 100644 index 8481bf1..0000000 --- a/Bind/src/Res/Icons/obfuscation.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Bind/src/Res/Icons/refresh.png b/Bind/src/Res/Icons/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..6d68b75b8602be2f952fc56925f0c3b382b98aa4 GIT binary patch literal 835 zcmV-J1HAl+P)^v(O5NtVlYM%#Ro%7eBuxI;@>bnnD}I(i4wn}iBU8dG$ABV6f`YrDV9R{ zD239}kL~Wxc!z=#o=n{2WM=NpIdjk4JA^R?|MQ85Z%-B`k_4hCf?zO|6GFTgI*DjO z6eE5h2Jx_Gl+vidVCcrR_rncmkGM^+UKH5Xx+?R*YNxruS}fLM%$iDT4Kqtw?_?x% znKCx0Yg)%9LHI*utkNhlZ|6~mtIlb*8VNC}c?uj7A(mRZxw@mpeo8}fS6iiwVwqa63DX<%O9fl$4o3Q0zA!1-VG)ga zMP=V2i8)m7LxE8(li}HQBU-PW--{>bKr#y4F61`__z4(L%_bqHX>=((9Z%_0A1zoD zK$0XcFZKPWOWl+=FFX_l*1G)82ih5|-*szd!sp z0|5Elg%&xJE8J`adVKz5$cKbaSa;xiB%|?=tIBpJo}+6tpPg3~CAu!4Zg`nj2*gqh zYV1YO`*47#H-zK|I$FU7>%s@k0Wgy7S?#d&) zlOdVa*z7E0A(uq}-=kMaHIwsW%dZoO;Mfm_<2Oe(?(HoxnU2{Hc=kx{dK*e|W*Xbu)-8W \ No newline at end of file diff --git a/Bind/src/Res/Icons/settings.png b/Bind/src/Res/Icons/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..0699492c523ff279a3c1448ba8cf7feb6f68aa74 GIT binary patch literal 810 zcmV+_1J(SAP)Y&FW5Ttq#q#_syLQzKHqohOFgD4clvM+`f=t=|G48^S}WVnjL z_RyXCFm+$v{X1uF<$D)?_Z-gecg{Wcf6kv{jKTjr0=>*;Gw?hQd3kxjaU4)lnM~Gz zR8>~zlO)NV<5H;_{L< zX&j}1i9(7-MIfJI#07)vu(axj#_n#N2+*79pscK{S*=#zA`A{%zj-{%%;9i)42Ig< z1oAVnvEFT?(KsH9#aNR{^=t&-oRIBc3Ja~`312f9>W_p%VbEx_`iY76dg7zeby)lQ z3WTIn?i@R1*6DQpk$8Oc5-wP#0G=wDD^}~Ug~q5<9)PK-H{WY(8x9j8uDy=IwnJ_* zorI@jZ=ra9s5lbgeNKg9_YiSO2(2bl^W*vX6{gV~dstLd^uXm>Vl$J&%z9Vw#{xa< z(Lg;rd!qC?X|p-I1u{F0W0K3|ZH0w}eFX&tN6C6V%(6*)6Qkt*9?o4?z3&NWNV|9c zb9dqpLWeLA@M3jbZk5fl&Cl%qP5#rFI_S!Bm59>ZJ<6;y-ZC1eB8$Bj?B`W8Hy+*s o+lv{tn2kw@EyOk={YQWS00n%6Wy2Jn8UO$Q07*qoM6N<$f>gGAWB>pF literal 0 HcmV?d00001 diff --git a/Bind/src/Res/Icons/settings.svg b/Bind/src/Res/Icons/settings.svg deleted file mode 100644 index 73f58d3..0000000 --- a/Bind/src/Res/Icons/settings.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Bind/src/Res/Icons/validation.png b/Bind/src/Res/Icons/validation.png new file mode 100644 index 0000000000000000000000000000000000000000..210b1a6c3ccefb387a9f902b012f61aa83eb775e GIT binary patch literal 724 zcmV;_0xSKAP)OzWUn z0;LtWZ01tXg<8j;Z_I_HKrTI+R#Ne#91TW8JU`7;ul=m;PH$CyJhs|ao-Hj8WYC4r z1T7}-!!nc)MnE)Beai(YB()laHos380a;w!sv)P1YS!k&(92_2?TrT5v`X%=E_V8$E7MsOQc!no)rgsMpO$rb~ znx$SzTE!+Dt2H= zl6+;~PXSRD8)o5><1;;jHvL`Gj?8DJ7yk74_!t{^`ruUtU*NUq$R?J_nf?quR*&`@ zqAX(S``lm9hd(j+Vh`pr|L6_Q^cyTSck9~%h(SgPu5Ui*zy$`64#J-28dg=`=1kAP zqZL \ No newline at end of file diff --git a/Bind/src/Res/Icons/verification.png b/Bind/src/Res/Icons/verification.png new file mode 100644 index 0000000000000000000000000000000000000000..eb476950f19916dfba9980ee1a071bd486232f4b GIT binary patch literal 773 zcmV+g1N!`lP)QOI*BL_|JqP>f{|vL-P6P^2`%z#CpQr zO_QIezLpylnB{SL4h*(kInCF^FbpITiG7>8uJan-g_Au#hrg>;I4LCoGGtC0qI;mS1?Tzs;YvI97?59h_A-+D*PC0Yik&C z^&_?t!^Y-Dfio3Tu(mW^%aL_gbRdEgFU~;AYhVS2XkSE>kZrWQ?xq_+HB^I`MA?DK zzAH*Hdna@gu~-bV{ZnA}A}q_YP-~%WYD|0(`Up#JaZzI}O z0lz1>juO?hhddH=P0J87Uv7}MKBW_iV)IC5@8Z>cP{1DvK3s&kEZH)RU%JAXN(Vd4 zwB96F66?;O6CDeeitseSjS$wk_}4S5JL{lY&XgCCZ_xr9Q|D7%l9Rbyt)BYuTrY5} z;$KpUv7m0|zWrW=+dftzA&J8z(dh`1nKoMxUUpCRJ3FenYXgpuyH>36lAhK-q}O+o z`6v|WE@rJb;uZv3YGB)+ \ No newline at end of file From 414d8458483bb98723a094b9a294b6971a20ff5b Mon Sep 17 00:00:00 2001 From: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com> Date: Fri, 26 Sep 2025 02:02:52 -0700 Subject: [PATCH 6/8] Update Bind.vcxproj Signed-off-by: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com> --- Bind/Bind.vcxproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Bind/Bind.vcxproj b/Bind/Bind.vcxproj index 7d01b1f..49319c2 100644 --- a/Bind/Bind.vcxproj +++ b/Bind/Bind.vcxproj @@ -69,8 +69,6 @@ $(QTDIR) - C:\Qt\5.15.2\msvc2019_64 - 5.15.2_msvc2019_64 @@ -462,4 +460,5 @@ - \ No newline at end of file + + From 24ba642770071f00a3c98f2db4ec1be38552d6aa Mon Sep 17 00:00:00 2001 From: WindowsAPI <82195276+WindowsAPI@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:02:52 -0700 Subject: [PATCH 7/8] Update build.yml --- .github/workflows/build.yml | 59 ++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a60f916..e3be38b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build Bind (SysCaller) +name: "Build SysCaller: Bind" on: push: @@ -378,34 +378,34 @@ jobs: - name: Upload Build Artifacts (Release) uses: actions/upload-artifact@v4 with: - name: Bind-x64-Release + name: Bind-Release path: Bind/x64/Release/ retention-days: 30 - name: Upload Build Artifacts (Debug) uses: actions/upload-artifact@v4 with: - name: Bind-x64-Debug + name: Bind-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" + $version = "v1.3.1-${{ github.run_number }}" + $zipName = "Bind-$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" + $version = "v1.3.1-${{ github.run_number }}" + $readmeText = "# SysCaller: Bind - v1.3.1`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 += "Platform: Windows x64 (64-bit)`n" + $readmeText += "Version: $version`n`n" $readmeText += "## Installation`n" $readmeText += "1. Download Bind.exe`n" $readmeText += "2. Run the executable`n`n" @@ -425,6 +425,43 @@ jobs: 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 + name: Release-Package + path: Bind/Bind-v1.3.1-*.zip retention-days: 90 + + - name: Create GitHub Release + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v1.3.1-${{ github.run_number }} + release_name: "SysCaller: Bind - v1.3.1-${{ github.run_number }}" + body: | + ## SysCaller: Bind - v1.3.1-${{ github.run_number }} + + **Build Date:** ${{ github.event.head_commit.timestamp }} + **Commit:** ${{ github.sha }} + **Platform:** Windows x64 (64-bit) + + ## Installation + 1. Download Bind.zip + 2. Replace the downloaded Bind directory with the one in SysCaller + 3. Extract the zip & run the Bind.exe + + ## Requirements + - Windows 10 or later + - Visual C++ Redistributable (if not already installed) + - Qt5 runtime libraries (if not already installed) + + - name: Upload Release Asset + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: Bind/Bind-v1.3.1-${{ github.run_number }}.zip + asset_name: Bind-v1.3.1-${{ github.run_number }}.zip + asset_content_type: application/zip From c0994a4dc7aee712c23c80fbcfa682352f3eec2b Mon Sep 17 00:00:00 2001 From: shikataganaii <138301021+shikataganaii@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:18:08 +0200 Subject: [PATCH 8/8] Update README for v1.3.1 and add credits section Bump version references from 1.3.0 to 1.3.1, note that the documentation website is currently down, and add a new credits section acknowledging key dependencies and contributors. --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3cace67..3fe07d6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SysCaller SDK v1.3.0 +# SysCaller SDK v1.3.1

SysCaller Logo @@ -12,7 +12,7 @@

-[![Version](https://img.shields.io/badge/Version-1.3.0-blue.svg)](https://github.com/micREsoft/SysCaller) +[![Version](https://img.shields.io/badge/Version-1.3.1-blue.svg)](https://github.com/micREsoft/SysCaller) [![License](https://img.shields.io/badge/License-GPLv3-green.svg)](LICENSE) [![Platform](https://img.shields.io/badge/Platform-Windows%2064--bit-lightgrey.svg)](https://github.com/micREsoft/SysCaller) [![C++](https://img.shields.io/badge/C%2B%2B-17%2B-blue.svg)](https://isocpp.org/) @@ -260,7 +260,8 @@ SysCaller includes full featured GUI "Bind" (formerly BuildTools) with capabilit ## Documentation -> [SysCaller Docs](https://reverseengineeri.ng) +> [SysCaller Docs](https://reverseengineeri.ng) | +> Website for documentation is currently down! > [SysCaller Nt Usage](https://github.com/micREsoft/SysCaller/tree/main/SysCaller/Wrapper/include) @@ -283,6 +284,18 @@ If you find SysCaller useful, consider starring the repo to help others discover --- +## Credits + +SysCaller wouldnt be possible without: + +- [PeParse](https://github.com/trailofbits/pe-parse) +- [CMark](https://github.com/commonmark/cmark) +- [Fugue Icons](https://p.yusukekamiyamane.com/) +- [Microsoft](https://microsoft.com) +- [NTDoc](https://ntdoc.m417z.com/) + +--- + ## License SysCaller is licensed under the GNU General Public License v3.0. See [LICENSE](LICENSE) for details.