├── 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 | [](https://github.com/surfboardv2ray/batch-fragment-test/releases/latest)
5 | [](https://github.com/surfboardv2ray/batch-fragment-test/releases/latest)
6 | [](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 | 
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 | 
51 | ## Stargazers
52 | [](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 |
--------------------------------------------------------------------------------