├── LICENSE ├── README.md ├── batch-fragment-test.ps1 └── config.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 SurfboardV2ray 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Batch Fragment Scanner

2 |

Batch Test v2ray fragment values to see which one works best on your network.

3 | 4 | [![Version](https://img.shields.io/github/v/release/surfboardv2ray/batch-fragment-test?label=Version&color=blue)](https://github.com/surfboardv2ray/batch-fragment-test/releases/latest) 5 | [![Download](https://img.shields.io/github/downloads/surfboardv2ray/batch-fragment-test/total?label=Downloads)](https://github.com/surfboardv2ray/batch-fragment-test/releases/latest) 6 | [![Stars](https://img.shields.io/github/stars/surfboardv2ray/batch-fragment-test?style=flat&label=Stars&color=tomato 7 | )](https://github.com/surfboardv2ray/batch-fragment-test) 8 | 9 | * ### راهنمای [🇮🇷 فارسی](https://telegra.ph/%D8%A7%D8%B3%DA%A9%D9%86%D8%B1-%D9%81%D8%B1%DA%AF%D9%85%D9%86%D8%AA-05-27) 10 | * ### [Android Termux/Linux](https://github.com/Ptechgithub/FragmentScanner) Version by P-Tech 11 | * ### [YouTube Video](https://www.youtube.com/watch?v=wL3-bRxM_2o) Instructions by V2rayIrani 12 | 13 | ![0](https://raw.githubusercontent.com/Ptechgithub/configs/main/media/line.gif) 14 | 15 | ## How to - Windows 10 16 | 17 | * Ensure v2rayN is closed 18 | 19 | To avoid conflicts, ensure any app using xray core is closed (otherwise the core process will be terminated) 20 | 21 | * Download Xray Core 22 | 23 | Downlaod `xray.exe` from the official [Xray-core Github Repository](https://github.com/XTLS/Xray-core/releases) and put it in the same folder as the powershell script. 24 | 25 | * Create a fragmented json config 26 | 27 | Create `config.json`, can be vmess, vless or trojan. Make sure it has "fragment" attributes too, values don't matter (use tools like [IRCF Space](https://fragment.github1.cloud/) to create fragment json config). Put it in the same folder as the powershell script as well. 28 | 29 | * Optional step: Edit the script for fragment values 30 | 31 | ✴️ Only if you desire, edit the .ps1 script at `Arrays of possible values` for "packets", "length", and "interval" based on your network. Values are used randomly in combination. 32 | 33 | 34 | * Run PowerShell script: 35 | 36 | 🧧 Right click and run the ps1 file with PowerShell. Confirm the Execution policy bypass via typing and sending `y` to run the script. 37 | 38 | ✅ Enter number of instances, i.e the rounds of random combination of fragment values. 39 | 40 | ✅ Enter the timeout for each ping test, i.e amount of time the test will wait for response. 41 | 42 | ✅ Enter HTTP Listening port. This depends on your json config. Default is 10809. 43 | 44 | ✅ Enter number of requests per instance, i.e how many times a fragment set of values should be tested. 45 | 46 | 🎆 After the code runs and finishes up, you'll get the top three (best) pings with their fragment values, and logs will be saved to `pings.txt`. The file contains response time with each fragment instance, with the average response time as well. 47 | 48 | 🎆 Xray Core Logs are saved separately to `xraylogs.txt` so be sure to check it out after the run for detailed info and possible errors. 49 | 50 | ![0](https://raw.githubusercontent.com/Ptechgithub/configs/main/media/line.gif) 51 | ## Stargazers 52 | [![Stargazers over time](https://starchart.cc/Surfboardv2ray/batch-fragment-scanner.svg?variant=adaptive)](https://starchart.cc/Surfboardv2ray/batch-fragment-scanner) 53 | -------------------------------------------------------------------------------- /batch-fragment-test.ps1: -------------------------------------------------------------------------------- 1 | # Check if running on Windows 10 or Windows 11 2 | $osVersion = [System.Environment]::OSVersion.Version 3 | 4 | if ($osVersion.Major -eq 10) { 5 | # For Windows 10, set execution policy to Bypass 6 | Set-ExecutionPolicy Bypass -Scope Process 7 | } elseif ($osVersion.Major -eq 11) { 8 | # For Windows 11, set execution policy to RemoteSigned 9 | Set-ExecutionPolicy RemoteSigned -Scope Process 10 | } else { 11 | Write-Host "Unsupported version of Windows. Exiting script." 12 | exit 13 | } 14 | 15 | # Path to the xray executable and config file in the same folder as the script 16 | $XRAY_PATH = Join-Path -Path $PSScriptRoot -ChildPath "xray.exe" 17 | $CONFIG_PATH = Join-Path -Path $PSScriptRoot -ChildPath "config.json" 18 | $LOG_FILE = Join-Path -Path $PSScriptRoot -ChildPath "pings.txt" 19 | $XRAY_LOG_FILE = Join-Path -Path $PSScriptRoot -ChildPath "xraylogs.txt" 20 | 21 | # Enclose paths with spaces in double quotes 22 | $XRAY_PATH = "$XRAY_PATH" 23 | $CONFIG_PATH = "$CONFIG_PATH" 24 | $LOG_FILE = "$LOG_FILE" 25 | $XRAY_LOG_FILE = "$XRAY_LOG_FILE" 26 | 27 | # Check if xray.exe exists 28 | if (-Not (Test-Path -Path $XRAY_PATH)) { 29 | Write-Host "Error: xray.exe not found" 30 | exit 31 | } 32 | 33 | # Create pings.txt and xraylogs.txt if they do not exist 34 | if (-Not (Test-Path -Path $LOG_FILE)) { 35 | New-Item -Path $LOG_FILE -ItemType File 36 | } 37 | if (-Not (Test-Path -Path $XRAY_LOG_FILE)) { 38 | New-Item -Path $XRAY_LOG_FILE -ItemType File 39 | } 40 | 41 | # Clear the content of the log files before running the tests 42 | Clear-Content -Path $LOG_FILE 43 | Clear-Content -Path $XRAY_LOG_FILE 44 | 45 | # Prompt user for input values with defaults 46 | $InstancesInput = Read-Host -Prompt "Enter the number of instances (default is 10)" 47 | $TimeoutSecInput = Read-Host -Prompt "Enter the timeout for each ping test in seconds (default is 10)" 48 | $HTTP_PROXY_PORTInput = Read-Host -Prompt "Enter the HTTP Listening port (default is 10809)" 49 | $PingCountInput = Read-Host -Prompt "Enter the number of requests per instance (default is 3)" 50 | 51 | # Set default values if inputs are empty 52 | $Instances = if ($InstancesInput) { [int]$InstancesInput } else { 10 } 53 | $TimeoutSec = if ($TimeoutSecInput) { [int]$TimeoutSecInput } else { 10 } 54 | $HTTP_PROXY_PORT = if ($HTTP_PROXY_PORTInput) { [int]$HTTP_PROXY_PORTInput } else { 10809 } 55 | $PingCount = if ($PingCountInput) { [int]$PingCountInput + 1 } else { 4 } # Add 1 to the user input to account for the extra request 56 | 57 | # HTTP Proxy server address 58 | $HTTP_PROXY_SERVER = "127.0.0.1" 59 | 60 | # Arrays of possible values for packets, length, and interval 61 | $packetsOptions = @("1-1", "1-2", "1-3", "1-5") 62 | $lengthOptions = @("1-1", "1-2", "1-3", "2-5", "1-5", "1-10", "3-5", "5-10", "3-10", "10-15", "10-30", "10-20", "20-50", "50-100", "100-150") 63 | $intervalOptions = @("1-1", "1-2", "3-5", "1-5", "5-10", "10-15", "10-20", "20-30", "20-50", "40-50", "50-100", "50-80", "100-150", "150-200", "100-200") 64 | 65 | # Calculate the maximum possible instances 66 | $maxPossibleInstances = $packetsOptions.Count * $lengthOptions.Count * $intervalOptions.Count 67 | 68 | # Validate user input for instances against the maximum possible instances 69 | while ($Instances -gt $maxPossibleInstances) { 70 | Write-Host "Error: Number of instances cannot be greater than the maximum possible instances ($maxPossibleInstances)" 71 | $InstancesInput = Read-Host -Prompt "Enter the number of instances (default is 10)" 72 | $Instances = if ($InstancesInput) { [int]$InstancesInput } else { 10 } 73 | } 74 | 75 | # Array to store top three lowest average response times 76 | $topThree = @() 77 | 78 | # Function to randomly select a value from an array 79 | function Get-RandomValue { 80 | param ( 81 | [array]$options 82 | ) 83 | 84 | $randomIndex = Get-Random -Minimum 0 -Maximum $options.Length 85 | return $options[$randomIndex] 86 | } 87 | 88 | # Function to generate a unique combination of packets, length, and interval values 89 | function Get-UniqueCombination { 90 | $combination = $null 91 | $usedCombinations = New-Object System.Collections.ArrayList 92 | 93 | do { 94 | $packets = Get-RandomValue -options $packetsOptions 95 | $length = Get-RandomValue -options $lengthOptions 96 | $interval = Get-RandomValue -options $intervalOptions 97 | 98 | $combination = "$packets,$length,$interval" 99 | } while ($usedCombinations -contains $combination) 100 | 101 | [void]$usedCombinations.Add($combination) 102 | 103 | return $packets, $length, $interval 104 | } 105 | 106 | # Function to modify config.json with random parameters 107 | function Modify-Config { 108 | param ( 109 | [string]$packets, 110 | [string]$length, 111 | [string]$interval 112 | ) 113 | 114 | $config = Get-Content -Path $CONFIG_PATH | Out-String | ConvertFrom-Json 115 | 116 | # Find the outbound with tag 'fragment' and modify its fragment settings 117 | $fragmentOutbound = $config.outbounds | Where-Object { $_.tag -eq 'fragment' } 118 | if ($fragmentOutbound -ne $null) { 119 | $fragmentOutbound.settings.fragment.packets = $packets 120 | $fragmentOutbound.settings.fragment.length = $length 121 | $fragmentOutbound.settings.fragment.interval = $interval 122 | } else { 123 | Write-Host "No 'fragment' outbound found in config.json" 124 | return 125 | } 126 | 127 | $config | ConvertTo-Json -Depth 100 | Set-Content -Path $CONFIG_PATH 128 | } 129 | 130 | # Function to stop the Xray process 131 | function Stop-XrayProcess { 132 | try { 133 | Stop-Process -Name "xray" -Force -ErrorAction Stop 134 | } catch { 135 | # Do not display "Xray process not found" message 136 | } 137 | } 138 | 139 | # Function to perform HTTP requests with proxy and measure response time 140 | function Send-HTTPRequest { 141 | param ( 142 | [int]$pingCount, 143 | [int]$timeout = $TimeoutSec * 1000 # Convert seconds to milliseconds 144 | ) 145 | 146 | # Set the target URL 147 | $url = "http://cp.cloudflare.com" 148 | 149 | # Initialize variables to store total time and count of pings 150 | $totalTime = 0 151 | $individualTimes = @() 152 | 153 | # Ping the specified number of times and measure the time for each ping 154 | for ($i = 1; $i -le $pingCount; $i++) { 155 | # Create a WebRequest object 156 | $request = [System.Net.HttpWebRequest]::Create($url) 157 | 158 | # Set the timeout for the request 159 | $request.Timeout = $timeout 160 | 161 | # Set the proxy settings 162 | $proxy = New-Object System.Net.WebProxy($HTTP_PROXY_SERVER, $HTTP_PROXY_PORT) 163 | $request.Proxy = $proxy 164 | 165 | try { 166 | $elapsedTime = Measure-Command { 167 | # Send the HTTP request and get the response 168 | $response = $request.GetResponse() 169 | } 170 | 171 | # Accumulate total time 172 | $totalTime += $elapsedTime.TotalMilliseconds 173 | $individualTimes += $elapsedTime.TotalMilliseconds 174 | } catch { 175 | $individualTimes += -1 # Mark failed requests with -1 176 | } 177 | 178 | # Add a 1-second delay between each ping 179 | Start-Sleep -Seconds 1 180 | } 181 | 182 | # Skip the first ping result 183 | $individualTimes = $individualTimes[1..($individualTimes.Count - 1)] 184 | 185 | # Calculate average ping time, considering -1 as timeout 186 | $validPings = $individualTimes | Where-Object { $_ -ne -1 } 187 | if ($validPings.Count -gt 0) { 188 | $totalValidTime = $validPings | Measure-Object -Sum | Select-Object -ExpandProperty Sum 189 | $averagePing = ($totalValidTime + ($individualTimes.Count - $validPings.Count) * $timeout) / ($pingCount - 1) 190 | } else { 191 | $averagePing = 0 192 | } 193 | 194 | # Log individual ping times to pings.txt 195 | Add-Content -Path $LOG_FILE -Value "Individual Ping Times:" 196 | Add-Content -Path $LOG_FILE -Value ($individualTimes -join ",") 197 | 198 | return $averagePing 199 | } 200 | 201 | # Main script 202 | # Clear the content of the log files before running the tests 203 | Clear-Content -Path $LOG_FILE 204 | Clear-Content -Path $XRAY_LOG_FILE 205 | 206 | # Create a table header 207 | $tableHeader = @" 208 | +--------------+-----------------+---------------+-----------------+---------------+ 209 | | Instance | Packets | Length | Interval | Average Ping | 210 | +--------------+-----------------+---------------+-----------------+---------------+ 211 | "@ 212 | 213 | # Write the table header to the console 214 | Write-Host $tableHeader 215 | 216 | for ($i = 0; $i -lt $Instances; $i++) { 217 | $packets, $length, $interval = Get-UniqueCombination 218 | 219 | Modify-Config -packets $packets -length $length -interval $interval 220 | 221 | # Stop Xray process if running 222 | Stop-XrayProcess 223 | 224 | # Start Xray process and redirect output to xraylogs.txt 225 | Start-Process -NoNewWindow -FilePath "$XRAY_PATH" -ArgumentList "-c `"$CONFIG_PATH`"" -RedirectStandardOutput "$XRAY_LOG_FILE" -RedirectStandardError "$XRAY_LOG_FILE.Error" 226 | 227 | Start-Sleep -Seconds 3 228 | 229 | Add-Content -Path $LOG_FILE -Value "Testing with packets=$packets, length=$length, interval=$interval..." 230 | $averagePing = Send-HTTPRequest -pingCount $PingCount 231 | Add-Content -Path $LOG_FILE -Value "Average Ping Time: $averagePing ms`n" 232 | 233 | # Add the average ping time along with fragment values to the top three list 234 | $topThree += [PSCustomObject]@{ 235 | Instance = $i + 1 236 | Packets = $packets 237 | Length = $length 238 | Interval = $interval 239 | AverageResponseTime = $averagePing 240 | } 241 | 242 | # Display the results in table format 243 | $averagePingRounded = "{0:N2}" -f $averagePing 244 | $tableRow = @" 245 | | {0,-9} | {1,-15} | {2,-13} | {3,-15} | {4,-13} | 246 | "@ -f ($i + 1), $packets, $length, $interval, $averagePingRounded 247 | Write-Host $tableRow 248 | 249 | # Add a one-second delay between each test instance 250 | Start-Sleep -Seconds 1 251 | } 252 | 253 | # Create a table footer 254 | $tableFooter = @" 255 | +--------------+-----------------+---------------+-----------------+---------------+ 256 | "@ 257 | # Write the table footer to the console 258 | Write-Host $tableFooter 259 | 260 | # Filter out entries with an average response time of 0 ms 261 | $validResults = $topThree | Where-Object { $_.AverageResponseTime -gt 0 } 262 | 263 | # Sort the top three list by average response time in ascending order 264 | $sortedTopThree = $validResults | Sort-Object -Property AverageResponseTime | Select-Object -First 3 265 | 266 | # Display the top three lowest average response times along with their corresponding fragment values 267 | Write-Host "Top three lowest average response times:" 268 | $sortedTopThree | Format-Table -Property Instance, Packets, Length, Interval, @{Name='AverageResponseTime (ms)';Expression={[math]::Round($_.AverageResponseTime, 2)}} 269 | 270 | # Stop Xray process if running 271 | Stop-XrayProcess 272 | 273 | # Prevent the PowerShell window from closing immediately 274 | Read-Host -Prompt "Press Enter to exit the script..." 275 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "remarks": "Surfboardv2ray", 3 | "log": { 4 | "access": "", 5 | "error": "", 6 | "loglevel": "warning" 7 | }, 8 | "inbounds": [ 9 | { 10 | "tag": "socks", 11 | "port": 10808, 12 | "listen": "127.0.0.1", 13 | "protocol": "socks", 14 | "sniffing": { 15 | "enabled": true, 16 | "destOverride": [ 17 | "http", 18 | "tls" 19 | ], 20 | "routeOnly": false 21 | }, 22 | "settings": { 23 | "auth": "noauth", 24 | "udp": true, 25 | "allowTransparent": false 26 | } 27 | }, 28 | { 29 | "tag": "http", 30 | "port": 10809, 31 | "listen": "127.0.0.1", 32 | "protocol": "http", 33 | "sniffing": { 34 | "enabled": true, 35 | "destOverride": [ 36 | "http", 37 | "tls" 38 | ], 39 | "routeOnly": false 40 | }, 41 | "settings": { 42 | "auth": "noauth", 43 | "udp": true, 44 | "allowTransparent": false 45 | } 46 | } 47 | ], 48 | "outbounds": [ 49 | { 50 | "tag": "proxy", 51 | "protocol": "vmess", 52 | "settings": { 53 | "vnext": [ 54 | { 55 | "address": "YOUR_ADDRESS", 56 | "port": 443, 57 | "users": [ 58 | { 59 | "id": "YOUR_UUID", 60 | "alterId": 0, 61 | "email": "t@t.tt", 62 | "security": "auto", 63 | "encryption": "none", 64 | "flow": "" 65 | } 66 | ] 67 | } 68 | ] 69 | }, 70 | "streamSettings": { 71 | "network": "ws", 72 | "security": "tls", 73 | "tlsSettings": { 74 | "allowInsecure": false, 75 | "serverName": "YOUR_SNI", 76 | "alpn": [ 77 | "h2", 78 | "http/1.1" 79 | ], 80 | "fingerprint": "chrome", 81 | "show": false 82 | }, 83 | "wsSettings": { 84 | "path": "/", 85 | "headers": { 86 | "Host": "YOUR_HOST" 87 | } 88 | }, 89 | "sockopt": { 90 | "dialerProxy": "fragment", 91 | "tcpKeepAliveIdle": 100, 92 | "mark": 255, 93 | "tcpNoDelay": true 94 | } 95 | } 96 | }, 97 | { 98 | "tag": "fragment", 99 | "protocol": "freedom", 100 | "settings": { 101 | "domainStrategy": "AsIs", 102 | "fragment": { 103 | "packets": "tlshello", 104 | "length": "10-20", 105 | "interval": "10-20" 106 | } 107 | }, 108 | "streamSettings": { 109 | "sockopt": { 110 | "tcpNoDelay": true, 111 | "tcpKeepAliveIdle": 100 112 | } 113 | } 114 | }, 115 | { 116 | "tag": "direct", 117 | "protocol": "freedom", 118 | "settings": {} 119 | }, 120 | { 121 | "tag": "block", 122 | "protocol": "blackhole", 123 | "settings": { 124 | "response": { 125 | "type": "http" 126 | } 127 | } 128 | } 129 | ], 130 | "routing": { 131 | "domainStrategy": "AsIs", 132 | "rules": [ 133 | { 134 | "type": "field", 135 | "inboundTag": [ 136 | "api" 137 | ], 138 | "outboundTag": "api", 139 | "enabled": true 140 | }, 141 | { 142 | "id": "5627785659655799759", 143 | "type": "field", 144 | "port": "0-65535", 145 | "outboundTag": "proxy", 146 | "enabled": true 147 | } 148 | ] 149 | } 150 | } 151 | --------------------------------------------------------------------------------