├── .gitignore
├── .gitattributes
├── assets
└── showcase.png
├── .idea
├── vcs.xml
├── .gitignore
├── modules.xml
├── go.imports.xml
├── vercel-botid.iml
├── icon.svg
└── inspectionProfiles
│ └── Project_Default.xml
├── cmd
└── main.go
├── LICENSE
├── botid
├── simplify.go
├── payload.go
├── crypto.go
├── tls.go
├── extract.go
└── botid.go
├── go.mod
├── readme.md
├── go.sum
└── webgl.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/assets/showcase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xKiian/vercel-botid/HEAD/assets/showcase.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/go.imports.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/vercel-botid.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/xkiian/vercel-botid/botid"
8 | )
9 |
10 | func main() {
11 | solver, err := botid.NewBotID("https://botid-testing-pi.vercel.app/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/a-4-a/c.js?i=0&v=3&h=botid-testing-pi.vercel.app")
12 | if err != nil {
13 | log.Fatal(err)
14 | }
15 |
16 | token, err := solver.GenerateToken()
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 |
21 | fmt.Println("[+]", token)
22 |
23 | fmt.Println(solver.Verify(token))
24 | }
25 |
--------------------------------------------------------------------------------
/.idea/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025
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.
--------------------------------------------------------------------------------
/botid/simplify.go:
--------------------------------------------------------------------------------
1 | package botid
2 |
3 | import (
4 | "github.com/t14raptor/go-fast/ast"
5 | )
6 |
7 | type FromCharCodeReplacerVisitor struct {
8 | ast.NoopVisitor
9 | }
10 |
11 | func (v *FromCharCodeReplacerVisitor) VisitExpression(n *ast.Expression) {
12 | n.VisitChildrenWith(v)
13 |
14 | callExpr, ok := n.Expr.(*ast.CallExpression)
15 | if !ok {
16 | return
17 | }
18 |
19 | if len(callExpr.ArgumentList) != 1 {
20 | return
21 | }
22 |
23 | callee, ok := callExpr.Callee.Expr.(*ast.MemberExpression)
24 | if !ok {
25 | return
26 | }
27 |
28 | prop, ok := callee.Property.Prop.(*ast.Identifier)
29 | if !ok {
30 | return
31 | }
32 |
33 | if prop.Name != "fromCharCode" {
34 | return
35 | }
36 |
37 | code := callExpr.ArgumentList[0]
38 | if numLit, ok := code.Expr.(*ast.NumberLiteral); ok {
39 | value := string(rune(int(numLit.Value)))
40 | *n = ast.Expression{
41 | Expr: &ast.StringLiteral{
42 | Value: value,
43 | },
44 | }
45 | }
46 | }
47 |
48 | func ReplaceFromCharCode(p *ast.Program) {
49 |
50 | r := &FromCharCodeReplacerVisitor{}
51 | r.V = r
52 | p.VisitWith(r)
53 | }
54 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/xkiian/vercel-botid
2 |
3 | go 1.25.1
4 |
5 | require (
6 | github.com/bogdanfinn/fhttp v0.6.3
7 | github.com/bogdanfinn/tls-client v1.11.2
8 | github.com/bogdanfinn/utls v1.7.4-barnius
9 | github.com/t14raptor/go-fast v0.0.4
10 | )
11 |
12 | require (
13 | github.com/andybalholm/brotli v1.1.1 // indirect
14 | github.com/bogdanfinn/quic-go-utls v1.0.4-utls // indirect
15 | github.com/cloudflare/circl v1.5.0 // indirect
16 | github.com/klauspost/compress v1.17.11 // indirect
17 | github.com/nukilabs/ftoa v1.0.0 // indirect
18 | github.com/nukilabs/unicodeid v0.1.0 // indirect
19 | github.com/quic-go/qpack v0.5.1 // indirect
20 | github.com/stretchr/testify v1.10.0 // indirect
21 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect
22 | github.com/xkiian/obfio-deobfuscator v0.0.0-20251210115027-1cdd8a96367e // indirect
23 | go.uber.org/mock v0.5.0 // indirect
24 | golang.org/x/crypto v0.36.0 // indirect
25 | golang.org/x/mod v0.18.0 // indirect
26 | golang.org/x/net v0.38.0 // indirect
27 | golang.org/x/sync v0.12.0 // indirect
28 | golang.org/x/sys v0.31.0 // indirect
29 | golang.org/x/text v0.23.0 // indirect
30 | golang.org/x/tools v0.22.0 // indirect
31 | )
32 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Vercel BotID Solver / "X-Is-Human" Header (basic mode)
2 |
3 |
4 |
5 |
6 |
7 | ---
8 |
9 | ## Overview
10 |
11 | This project provides a Golang-based solver for the Vercel BotID challenge.
12 | It uses go-fAST to deobfuscate and then extract every value required. (blazingly fast)
13 |
14 | ---
15 |
16 | # ⭐ Show your Support
17 |
18 | Please star the repository if you find it useful! Your support helps improve the project. ❤️
19 |
20 | ---
21 |
22 | ## Installation
23 |
24 | ```
25 | go get github.com/xkiian/vercel-botid && go mod tidy
26 | ```
27 |
28 | ## Usage
29 |
30 | ```go
31 | package main
32 |
33 | import (
34 | "fmt"
35 | "log"
36 | "github.com/xkiian/vercel-botid/botid"
37 | )
38 |
39 | func main() {
40 | solver, err := botid.NewBotID("SCRIPT URL HERE (see tutorial for help)")
41 | if err != nil {
42 | log.Fatal(err)
43 | }
44 |
45 | token, err := solver.GenerateToken()
46 | if err != nil {
47 | log.Fatal(err)
48 | }
49 | fmt.Println("Generated BotID Token:", token)
50 | }
51 | ```
52 |
53 | Note: You will need to provide your own webgl fingerprints. Examples can be found in [webgl.json](webgl.json)
54 | (You only need webgl_unmasked_vendor & webgl_unmasked_renderer)
55 |
56 | ## License
57 |
58 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
59 |
60 | ---
61 |
62 | ## Disclaimer
63 |
64 | This package is **unofficial** and not affiliated with Vercel. Use it responsibly and in accordance with AWS's terms of
65 | service.
66 |
67 |
--------------------------------------------------------------------------------
/botid/payload.go:
--------------------------------------------------------------------------------
1 | package botid
2 |
3 | import (
4 | "encoding/json"
5 | "math/rand"
6 | "os"
7 | )
8 |
9 | type WebGL struct {
10 | WebGLUnmaskedVendor string `json:"webgl_unmasked_vendor"`
11 | }
12 | type GPUInfo struct {
13 | WebGLRenderer string `json:"webgl_unmasked_renderer"`
14 | WebGL []WebGL `json:"webgl"`
15 | }
16 |
17 | type FpPayload struct {
18 | DomController bool `json:"p"`
19 | Seed float64 `json:"S"`
20 | GpuVendor struct {
21 | UnmaskedVendorWebgl string `json:"v"`
22 | UnmaskedRendererWebgl string `json:"r"`
23 | } `json:"w"`
24 | Selenium bool `json:"s"`
25 | Headless bool `json:"h"`
26 | Devtools bool `json:"b"`
27 | Devtools2 bool `json:"d"`
28 | }
29 |
30 | type Payload struct {
31 | Arg1 float64 `json:"b"`
32 | Rand float64 `json:"v"`
33 | Signature string `json:"e"`
34 | Fp string `json:"s"`
35 | Arg2 float64 `json:"d"`
36 | Version string `json:"vr"`
37 | }
38 |
39 | var gpus []GPUInfo
40 |
41 | func init() {
42 | file, err := os.Open("webgl.json")
43 | if err != nil {
44 | panic(err)
45 | }
46 | defer file.Close()
47 |
48 | decoder := json.NewDecoder(file)
49 | err = decoder.Decode(&gpus)
50 | if err != nil {
51 | panic(err)
52 | }
53 | }
54 |
55 | func buildFp(key string, seed float64) (string, error) {
56 | gpu := gpus[rand.Intn(len(gpus))]
57 | fp := FpPayload{
58 | DomController: false,
59 | Seed: seed,
60 | GpuVendor: struct {
61 | UnmaskedVendorWebgl string `json:"v"`
62 | UnmaskedRendererWebgl string `json:"r"`
63 | }{
64 | UnmaskedVendorWebgl: gpu.WebGL[0].WebGLUnmaskedVendor,
65 | UnmaskedRendererWebgl: gpu.WebGLRenderer,
66 | },
67 | Selenium: false,
68 | Headless: false,
69 | Devtools: false,
70 | Devtools2: false,
71 | }
72 |
73 | return Encrypt(key, fp)
74 | }
75 |
76 | func BuildPayload(ctx *ScriptCtx) (*Payload, error) {
77 | fp, err := buildFp(ctx.key, ctx.seed)
78 | if err != nil {
79 | return nil, err
80 | }
81 | return &Payload{
82 | Arg1: ctx.arg1,
83 | Rand: ctx.rand,
84 | Signature: ctx.signature,
85 | Fp: fp,
86 | Arg2: ctx.arg2,
87 | Version: ctx.version,
88 | }, nil
89 | }
90 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/botid/crypto.go:
--------------------------------------------------------------------------------
1 | package botid
2 |
3 | import (
4 | "crypto/aes"
5 | "crypto/cipher"
6 | "crypto/pbkdf2"
7 | "crypto/rand"
8 | "crypto/sha256"
9 | "encoding/base64"
10 | "encoding/json"
11 | "errors"
12 | "fmt"
13 | "io"
14 | )
15 |
16 | func Encrypt(password string, data any) (string, error) {
17 | salt := make([]byte, 16)
18 | if _, err := io.ReadFull(rand.Reader, salt); err != nil {
19 | return "", fmt.Errorf("failed to generate salt: %w", err)
20 | }
21 |
22 | iv := make([]byte, 12)
23 | if _, err := io.ReadFull(rand.Reader, iv); err != nil {
24 | return "", fmt.Errorf("failed to generate IV: %w", err)
25 | }
26 |
27 | key, _ := pbkdf2.Key(sha256.New, password, salt, 100000, 32)
28 |
29 | block, err := aes.NewCipher(key)
30 | if err != nil {
31 | return "", fmt.Errorf("failed to create AES cipher: %w", err)
32 | }
33 |
34 | gcm, err := cipher.NewGCM(block)
35 | if err != nil {
36 | return "", fmt.Errorf("failed to create GCM: %w", err)
37 | }
38 |
39 | jsonData, err := json.Marshal(data)
40 | if err != nil {
41 | return "", fmt.Errorf("failed to marshal data to JSON: %w", err)
42 | }
43 |
44 | ciphertext := gcm.Seal(nil, iv, jsonData, nil)
45 |
46 | result := make([]byte, 0, len(salt)+len(iv)+len(ciphertext))
47 | result = append(result, salt...)
48 | result = append(result, iv...)
49 | result = append(result, ciphertext...)
50 |
51 | return base64.StdEncoding.EncodeToString(result), nil
52 | }
53 |
54 | func Decrypt(password string, encryptedData string) error {
55 | data, err := base64.StdEncoding.DecodeString(encryptedData)
56 | if err != nil {
57 | return fmt.Errorf("failed to decode base64: %w", err)
58 | }
59 |
60 | if len(data) < 44 {
61 | return errors.New("encrypted data too short")
62 | }
63 |
64 | salt := data[:16]
65 |
66 | iv := data[16:28]
67 |
68 | ciphertext := data[28:]
69 |
70 | key, _ := pbkdf2.Key(sha256.New, password, salt, 100000, 32)
71 |
72 | block, err := aes.NewCipher(key)
73 | if err != nil {
74 | return fmt.Errorf("failed to create AES cipher: %w", err)
75 | }
76 |
77 | gcm, err := cipher.NewGCM(block)
78 | if err != nil {
79 | return fmt.Errorf("failed to create GCM: %w", err)
80 | }
81 |
82 | plaintext, err := gcm.Open(nil, iv, ciphertext, nil)
83 | if err != nil {
84 | return fmt.Errorf("failed to decrypt data: %w", err)
85 | }
86 | fmt.Println(string(plaintext))
87 |
88 | return nil
89 | }
90 |
--------------------------------------------------------------------------------
/botid/tls.go:
--------------------------------------------------------------------------------
1 | package botid
2 |
3 | import (
4 | "github.com/bogdanfinn/fhttp/http2"
5 | "github.com/bogdanfinn/tls-client/profiles"
6 | tls "github.com/bogdanfinn/utls"
7 | )
8 |
9 | var Brave_144 = profiles.NewClientProfile(
10 | tls.ClientHelloID{
11 | Client: "Brave_144",
12 | Version: "1",
13 | Seed: nil,
14 | SpecFactory: func() (tls.ClientHelloSpec, error) {
15 | return tls.ClientHelloSpec{
16 | CipherSuites: []uint16{
17 | tls.GREASE_PLACEHOLDER,
18 | tls.TLS_AES_128_GCM_SHA256,
19 | tls.TLS_AES_256_GCM_SHA384,
20 | tls.TLS_CHACHA20_POLY1305_SHA256,
21 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
22 | tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
23 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
24 | tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
25 | tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
26 | tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
27 | tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
28 | tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
29 | tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
30 | tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
31 | tls.TLS_RSA_WITH_AES_128_CBC_SHA,
32 | tls.TLS_RSA_WITH_AES_256_CBC_SHA,
33 | },
34 | // CompressionMethods is not implemented by tls.peet.ws, check manually
35 | CompressionMethods: []uint8{
36 | tls.CompressionNone,
37 | },
38 | Extensions: []tls.TLSExtension{
39 | &tls.UtlsGREASEExtension{},
40 | &tls.ExtendedMasterSecretExtension{},
41 | &tls.SupportedCurvesExtension{[]tls.CurveID{
42 | tls.CurveID(tls.GREASE_PLACEHOLDER),
43 | 4588 /* X25519MLKEM768 (4588) */,
44 | tls.X25519,
45 | tls.CurveP256,
46 | tls.CurveP384,
47 | }},
48 | &tls.SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []tls.SignatureScheme{
49 | tls.ECDSAWithP256AndSHA256,
50 | tls.PSSWithSHA256,
51 | tls.PKCS1WithSHA256,
52 | tls.ECDSAWithP384AndSHA384,
53 | tls.PSSWithSHA384,
54 | tls.PKCS1WithSHA384,
55 | tls.PSSWithSHA512,
56 | tls.PKCS1WithSHA512,
57 | }},
58 | &tls.PSKKeyExchangeModesExtension{[]uint8{
59 | tls.PskModeDHE,
60 | }},
61 | &tls.SupportedVersionsExtension{[]uint16{
62 | tls.GREASE_PLACEHOLDER,
63 | tls.VersionTLS13,
64 | tls.VersionTLS12,
65 | }},
66 | tls.BoringGREASEECH(),
67 | &tls.ApplicationSettingsExtension{
68 | //codePoint: tls.ExtensionALPSOld,
69 | SupportedProtocols: []string{"h2"},
70 | },
71 | &tls.SupportedPointsExtension{SupportedPoints: []byte{0x00}},
72 | &tls.UtlsCompressCertExtension{[]tls.CertCompressionAlgo{
73 | tls.CertCompressionBrotli,
74 | }},
75 | &tls.ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}},
76 | &tls.RenegotiationInfoExtension{Renegotiation: tls.RenegotiateNever},
77 | &tls.SCTExtension{},
78 | &tls.SessionTicketExtension{},
79 | &tls.SNIExtension{},
80 | &tls.StatusRequestExtension{},
81 | &tls.KeyShareExtension{[]tls.KeyShare{
82 | {Group: tls.CurveID(tls.GREASE_PLACEHOLDER), Data: []byte{0} /* TLS_GREASE (0xaaaa) */},
83 | {Group: 4588 /* X25519MLKEM768 (4588) */},
84 | {Group: tls.X25519},
85 | }},
86 | &tls.UtlsGREASEExtension{},
87 | &tls.UtlsPreSharedKeyExtension{OmitEmptyPsk: true},
88 | },
89 | }, nil
90 | },
91 | },
92 | map[http2.SettingID]uint32{
93 | http2.SettingHeaderTableSize: 65536,
94 | http2.SettingEnablePush: 0,
95 | http2.SettingInitialWindowSize: 6291456,
96 | http2.SettingMaxHeaderListSize: 262144,
97 | },
98 | []http2.SettingID{
99 | http2.SettingHeaderTableSize,
100 | http2.SettingEnablePush,
101 | http2.SettingInitialWindowSize,
102 | http2.SettingMaxHeaderListSize,
103 | },
104 | []string{
105 | ":method",
106 | ":authority",
107 | ":scheme",
108 | ":path",
109 | },
110 | uint32(15663105),
111 | []http2.Priority{},
112 | &http2.PriorityParam{
113 | StreamDep: 0,
114 | Exclusive: true,
115 | Weight: 255,
116 | },
117 | )
118 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
2 | github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
3 | github.com/bogdanfinn/fhttp v0.6.3 h1:WtWurH0jbc1X2hOhWckVDiSQPonaU/WwjInB+OwnaCI=
4 | github.com/bogdanfinn/fhttp v0.6.3/go.mod h1:0irhEtS+wJ4m8SGhWO0wmbXMjCbH3WZpU6UcymRYKuk=
5 | github.com/bogdanfinn/quic-go-utls v1.0.4-utls h1:zPjusVVNeJFA2ORMAP0rjnrZrBkV4Dnia4e6ToOfUDA=
6 | github.com/bogdanfinn/quic-go-utls v1.0.4-utls/go.mod h1:UONJOaHGWho08kZtkkgH7GjktEPjMemGxjTcNpVPZVA=
7 | github.com/bogdanfinn/tls-client v1.11.2 h1:o6qX0L1cEi+4MaBqujxqOeK254VZM20t3QR+A34/V6I=
8 | github.com/bogdanfinn/tls-client v1.11.2/go.mod h1:qQIsVGe35NdxYEozNh9JuDZ+aOaOEq2tKAsu2iYEGZg=
9 | github.com/bogdanfinn/utls v1.7.4-barnius h1:1ldNJEpKdkrx7b8hEc6MRkjnZIF8f2lDcTtRVxqY9zw=
10 | github.com/bogdanfinn/utls v1.7.4-barnius/go.mod h1:SUn0CoHGVp/akGNuaqh99yvovu64PCP2LbWd3Z/Laic=
11 | github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
12 | github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
13 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
14 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15 | github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
16 | github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
17 | github.com/nukilabs/ftoa v1.0.0 h1:AiCb9begI2rwjDQm7/H+ce1ctdkixnOOw/bpurZTySc=
18 | github.com/nukilabs/ftoa v1.0.0/go.mod h1:ZJE2wT+SozGrxoj3oRB6Q6Kl3IMpCzdNOhymdmLGrHU=
19 | github.com/nukilabs/unicodeid v0.1.0 h1:+CtAaV9CkgVA3Jy9V29yb57DPCLsnWUJrP9LxhkD89w=
20 | github.com/nukilabs/unicodeid v0.1.0/go.mod h1:3qxjfcmcgVYBg9dMRNSniAVfqxwOed7JU7P2rPkJrRg=
21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
23 | github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
24 | github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
25 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
26 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
27 | github.com/t14raptor/go-fast v0.0.4 h1:48k3hIS7rx20iq+kJx39XpKaBi5k+etCtl9AXHRd0O8=
28 | github.com/t14raptor/go-fast v0.0.4/go.mod h1:IyzuItbJu1DY1z+xBRE9Bk/jCvaJEFG/yeSupi/2HDU=
29 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc=
30 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng=
31 | github.com/xkiian/obfio-deobfuscator v0.0.0-20251210115027-1cdd8a96367e h1:wo3YOk4Kzj41Q0EOLt0+gGiC59/Ht9MpjJOqf884HPo=
32 | github.com/xkiian/obfio-deobfuscator v0.0.0-20251210115027-1cdd8a96367e/go.mod h1:zo6Ks/6s2keW4xwUbcXzUDRioVn96j7Rr/luy05gtgE=
33 | github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
34 | github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
35 | go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
36 | go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
37 | golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
38 | golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
39 | golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
40 | golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
41 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
42 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
43 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
44 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
45 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
46 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
47 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
48 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
49 | golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
50 | golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
51 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
52 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
53 |
--------------------------------------------------------------------------------
/botid/extract.go:
--------------------------------------------------------------------------------
1 | package botid
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/t14raptor/go-fast/ast"
7 | "github.com/t14raptor/go-fast/generator"
8 | "github.com/t14raptor/go-fast/parser"
9 | "github.com/t14raptor/go-fast/transform/simplifier"
10 | deobf "github.com/xkiian/obfio-deobfuscator"
11 | )
12 |
13 | func ExtractFromScript(script *string) (*ScriptCtx, error) {
14 | parsed, err := parser.ParseFile(*script)
15 | if err != nil {
16 | return nil, err
17 | }
18 |
19 | deobf.Deobfuscate(parsed) // magic!!
20 | simplifier.Simplify(parsed, false)
21 | ReplaceFromCharCode(parsed)
22 | simplifier.Simplify(parsed, false) //its just for string addition but i cba
23 |
24 | //os.WriteFile("out.js", []byte(generator.Generate(parsed)), 0644)
25 |
26 | return runExtractionVisitor(parsed), nil
27 | }
28 |
29 | type ScriptCtx struct {
30 | ast.NoopVisitor
31 |
32 | assignments map[ast.Id]string
33 |
34 | key string
35 | seed float64
36 |
37 | arg1 float64
38 | arg2 float64
39 | rand float64
40 | signature string
41 | version string
42 | }
43 |
44 | func (v *ScriptCtx) VisitAssignExpression(n *ast.AssignExpression) {
45 | n.VisitChildrenWith(v)
46 | right, ok := n.Right.Expr.(*ast.StringLiteral)
47 | if !ok {
48 | return
49 | }
50 |
51 | left, ok := n.Left.Expr.(*ast.Identifier)
52 | if !ok {
53 | return
54 | }
55 |
56 | v.assignments[left.ToId()] = right.Value
57 | }
58 |
59 | func (v *ScriptCtx) VisitCallExpression(n *ast.CallExpression) {
60 | n.VisitChildrenWith(v)
61 |
62 | args := n.ArgumentList
63 |
64 | switch len(args) {
65 | case 5:
66 | arg1, ok := args[0].Expr.(*ast.NumberLiteral)
67 | if !ok {
68 | return
69 | }
70 | v.arg1 = arg1.Value
71 |
72 | arg2, ok := args[1].Expr.(*ast.NumberLiteral)
73 | if !ok {
74 | return
75 | }
76 | v.arg2 = arg2.Value
77 |
78 | arg3, ok := args[2].Expr.(*ast.NumberLiteral)
79 | if !ok {
80 | return
81 | }
82 | v.rand = arg3.Value
83 |
84 | arg4, ok := args[3].Expr.(*ast.StringLiteral)
85 | if !ok {
86 | return
87 | }
88 | v.signature = arg4.Value
89 |
90 | arg5, ok := args[4].Expr.(*ast.StringLiteral)
91 | if !ok {
92 | return
93 | }
94 | v.version = arg5.Value
95 | case 2:
96 |
97 | callExpr, ok := args[0].Expr.(*ast.CallExpression)
98 | if !ok {
99 | return
100 | }
101 |
102 | callee, ok := callExpr.Callee.Expr.(*ast.MemberExpression)
103 | if !ok {
104 | return
105 | }
106 |
107 | prop, ok := callee.Property.Prop.(*ast.Identifier)
108 | if !ok {
109 | return
110 | }
111 | if prop.Name != "join" {
112 | return
113 | }
114 |
115 | elements, ok := callee.Object.Expr.(*ast.ArrayLiteral)
116 | if !ok {
117 | return
118 | }
119 |
120 | first, ok := elements.Value[0].Expr.(*ast.Identifier)
121 | if !ok {
122 | return
123 | }
124 | found := v.assignments[first.ToId()]
125 | if found == "" {
126 | return
127 | }
128 | v.key = found
129 |
130 | second, ok := elements.Value[1].Expr.(*ast.Identifier)
131 | if !ok {
132 | return
133 | }
134 | found = v.assignments[second.ToId()]
135 | if found == "" {
136 | return
137 | }
138 | v.key += found
139 |
140 | /*if callExpr.Operator.String() != "+" {
141 | return
142 | }
143 |
144 | left, ok := callExpr.Left.Expr.(*ast.Identifier)
145 | if !ok {
146 | return
147 | }
148 | found := v.assignments[left.ToId()]
149 | fmt.Print(found)
150 | if found == "" {
151 | return
152 | }
153 | v.key = found
154 |
155 | right, ok := callExpr.Right.Expr.(*ast.Identifier)
156 | if !ok {
157 | return
158 | }
159 | found = v.assignments[right.ToId()]
160 | fmt.Print(found)
161 | if found == "" {
162 | return
163 | }
164 | v.key += found*/
165 | }
166 | }
167 |
168 | func (v *ScriptCtx) VisitObjectLiteral(n *ast.ObjectLiteral) {
169 | n.VisitChildrenWith(v)
170 | if !strings.Contains(generator.Generate(n), "(window)") {
171 | return
172 | }
173 |
174 | for _, prop := range n.Value {
175 | propKeyed, ok := prop.Prop.(*ast.PropertyKeyed)
176 | if !ok {
177 | continue
178 | }
179 |
180 | strLit, ok := propKeyed.Key.Expr.(*ast.StringLiteral)
181 | if !ok {
182 | continue
183 | }
184 |
185 | if strLit.Value != "S" { //maybe needs better filtering in the future
186 | continue
187 | }
188 | num, ok := propKeyed.Value.Expr.(*ast.NumberLiteral)
189 | if !ok {
190 | continue
191 | }
192 |
193 | v.seed = num.Value
194 | return
195 | }
196 |
197 | }
198 |
199 | func runExtractionVisitor(program *ast.Program) *ScriptCtx {
200 | f := &ScriptCtx{
201 | assignments: make(map[ast.Id]string),
202 | }
203 | f.V = f
204 | program.VisitWith(f)
205 | return f
206 | }
207 |
--------------------------------------------------------------------------------
/botid/botid.go:
--------------------------------------------------------------------------------
1 | package botid
2 |
3 | import (
4 | "encoding/json"
5 | "io"
6 |
7 | http "github.com/bogdanfinn/fhttp"
8 | tlsclient "github.com/bogdanfinn/tls-client"
9 | )
10 |
11 | type BotID struct {
12 | client tlsclient.HttpClient
13 | scriptUrl string
14 | }
15 |
16 | func NewBotID(scriptUrl string) (*BotID, error) {
17 | jar := tlsclient.NewCookieJar()
18 | options := []tlsclient.HttpClientOption{
19 | tlsclient.WithTimeoutSeconds(30),
20 | tlsclient.WithClientProfile(Brave_144),
21 | tlsclient.WithCookieJar(jar),
22 | }
23 |
24 | client, err := tlsclient.NewHttpClient(tlsclient.NewNoopLogger(), options...)
25 | if err != nil {
26 | return nil, err
27 | }
28 | client.SetFollowRedirect(true)
29 | return &BotID{client: client, scriptUrl: scriptUrl}, nil
30 | }
31 |
32 | func (bot *BotID) FetchScript() (*string, error) {
33 | req, err := http.NewRequest(http.MethodGet, bot.scriptUrl, nil)
34 | if err != nil {
35 | return nil, err
36 | }
37 | req.Header = http.Header{
38 | "sec-ch-ua": {"\"Chromium\";v=\"142\", \"Brave\";v=\"142\", \"Not_A Brand\";v=\"99\""},
39 | "sec-ch-ua-mobile": {"?0"},
40 | "sec-ch-ua-platform": {"\"Windows\""},
41 | "upgrade-insecure-requests": {"1"},
42 | "user-agent": {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"},
43 | "accept": {"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8"},
44 | "sec-gpc": {"1"},
45 | "accept-language": {"en-US,en;q=0.5"},
46 | "sec-fetch-site": {"none"},
47 | "sec-fetch-mode": {"navigate"},
48 | "sec-fetch-user": {"?1"},
49 | "sec-fetch-dest": {"document"},
50 | "accept-encoding": {"gzip, deflate, br, zstd"},
51 | "priority": {"u=0, i"},
52 | http.HeaderOrderKey: {"sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "upgrade-insecure-requests", "user-agent", "accept", "sec-gpc", "accept-language", "sec-fetch-site", "sec-fetch-mode", "sec-fetch-user", "sec-fetch-dest", "accept-encoding", "priority"},
53 | }
54 |
55 | resp, err := bot.client.Do(req)
56 | if err != nil {
57 | return nil, err
58 | }
59 |
60 | defer resp.Body.Close()
61 |
62 | body, err := io.ReadAll(resp.Body)
63 | if err != nil {
64 | return nil, err
65 | }
66 |
67 | bodyString := string(body)
68 |
69 | return &bodyString, nil
70 | }
71 |
72 | // Verify | this is just if you use the example script url. Don't use this if you modify the example script url!!!!
73 | func (bot *BotID) Verify(token string) (string, error) {
74 | req, err := http.NewRequest(http.MethodPost, "https://botid-testing-pi.vercel.app/api/contact/test", nil)
75 | if err != nil {
76 | return "", err
77 | }
78 |
79 | req.Header = http.Header{
80 | "content-length": {"0"},
81 | "sec-ch-ua-platform": {"\"Windows\""},
82 | "x-is-human": {token},
83 | "x-path": {"/api/generate"},
84 | "user-agent": {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"},
85 | "sec-ch-ua": {"\"Chromium\";v=\"142\", \"Brave\";v=\"142\", \"Not_A Brand\";v=\"99\""},
86 | "x-method": {"POST"},
87 | "sec-ch-ua-mobile": {"?0"},
88 | "accept": {"*/*"},
89 | "sec-gpc": {"1"},
90 | "accept-language": {"en-US,en;q=0.5"},
91 | "origin": {"https://botid-testing-pi.vercel.app"},
92 | "sec-fetch-site": {"same-origin"},
93 | "sec-fetch-mode": {"cors"},
94 | "sec-fetch-dest": {"empty"},
95 | "referer": {"https://botid-testing-pi.vercel.app/"},
96 | "accept-encoding": {"gzip, deflate, br, zstd"},
97 | "priority": {"u=1, i"},
98 | http.HeaderOrderKey: {"content-length", "sec-ch-ua-platform", "x-is-human", "x-path", "user-agent", "sec-ch-ua", "x-method", "sec-ch-ua-mobile", "accept", "sec-gpc", "accept-language", "origin", "sec-fetch-site", "sec-fetch-mode", "sec-fetch-dest", "referer", "accept-encoding", "priority"},
99 | }
100 |
101 | resp, err := bot.client.Do(req)
102 | if err != nil {
103 | return "", err
104 | }
105 |
106 | defer resp.Body.Close()
107 |
108 | body, err := io.ReadAll(resp.Body)
109 | if err != nil {
110 | return "", err
111 | }
112 |
113 | return string(body), nil
114 | }
115 |
116 | func (bot *BotID) GenerateToken() (string, error) {
117 | script, err := bot.FetchScript()
118 | if err != nil {
119 | return "", err
120 | }
121 |
122 | ctx, err := ExtractFromScript(script)
123 | if err != nil {
124 | return "", err
125 | }
126 |
127 | payload, err := BuildPayload(ctx)
128 | if err != nil {
129 | return "", err
130 | }
131 |
132 | encoded, err := json.Marshal(payload)
133 | if err != nil {
134 | return "", err
135 | }
136 |
137 | return string(encoded), nil
138 | }
139 |
--------------------------------------------------------------------------------
/webgl.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "webgl_unmasked_renderer": "ANGLE (Apple, ANGLE Metal Renderer: Apple M2 Pro, Unspecified Version)",
4 | "webgl": [
5 | {
6 | "webgl_extensions": "ANGLE_instanced_arrays;EXT_blend_minmax;EXT_clip_control;EXT_color_buffer_half_float;EXT_depth_clamp;EXT_disjoint_timer_query;EXT_float_blend;EXT_frag_depth;EXT_polygon_offset_clamp;EXT_shader_texture_lod;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;EXT_texture_mirror_clamp_to_edge;EXT_sRGB;KHR_parallel_shader_compile;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_blend_func_extended;WEBGL_color_buffer_float;WEBGL_compressed_texture_astc;WEBGL_compressed_texture_etc;WEBGL_compressed_texture_etc1;WEBGL_compressed_texture_pvrtc;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_multi_draw;WEBGL_polygon_mode",
7 | "webgl_extensions_hash": "9cbeeda2b4ce5415b07e1d1e43783a58",
8 | "webgl_renderer": "WebKit WebGL",
9 | "webgl_vendor": "WebKit",
10 | "webgl_version": "WebGL 1.0 (OpenGL ES 2.0 Chromium)",
11 | "webgl_shading_language_version": "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)",
12 | "webgl_aliased_line_width_range": "[1, 1]",
13 | "webgl_aliased_point_size_range": "[1, 511]",
14 | "webgl_antialiasing": true,
15 | "webgl_bits": "8,8,24,8,8,0",
16 | "webgl_max_params": "16,32,16384,1024,16384,16,16,30,16,16,1024",
17 | "webgl_max_viewport_dims": "[16384, 16384]",
18 | "webgl_unmasked_vendor": "Google Inc. (Apple)",
19 | "webgl_unmasked_renderer": "ANGLE (Apple, ANGLE Metal Renderer: Apple M2 Pro, Unspecified Version)",
20 | "webgl_vsf_params": "23,127,127,23,127,127,23,127,127",
21 | "webgl_vsi_params": "0,31,30,0,31,30,0,31,30",
22 | "webgl_fsf_params": "23,127,127,23,127,127,23,127,127",
23 | "webgl_fsi_params": "0,31,30,0,31,30,0,31,30",
24 | "webgl_hash_webgl": "a5c294663e62715a685b8a5f7d436da2"
25 | }
26 | ]
27 | },
28 | {
29 | "webgl_unmasked_renderer": "ANGLE (AMD, AMD Radeon(TM) Graphics (0x00001681) Direct3D11 vs_5_0 ps_5_0, D3D11)",
30 | "webgl": [
31 | {
32 | "webgl_extensions": "ANGLE_instanced_arrays;EXT_blend_minmax;EXT_clip_control;EXT_color_buffer_half_float;EXT_depth_clamp;EXT_disjoint_timer_query;EXT_float_blend;EXT_frag_depth;EXT_polygon_offset_clamp;EXT_shader_texture_lod;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;EXT_texture_mirror_clamp_to_edge;EXT_sRGB;KHR_parallel_shader_compile;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_blend_func_extended;WEBGL_color_buffer_float;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_multi_draw;WEBGL_polygon_mode",
33 | "webgl_extensions_hash": "7300c23f4e6fa34e534fc99c1b628588",
34 | "webgl_renderer": "WebKit WebGL",
35 | "webgl_vendor": "WebKit",
36 | "webgl_version": "WebGL 1.0 (OpenGL ES 2.0 Chromium)",
37 | "webgl_shading_language_version": "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)",
38 | "webgl_aliased_line_width_range": "[1, 1]",
39 | "webgl_aliased_point_size_range": "[1, 1024]",
40 | "webgl_antialiasing": true,
41 | "webgl_bits": "8,8,24,8,8,0",
42 | "webgl_max_params": "16,32,16384,1024,16384,16,16,30,16,16,4096",
43 | "webgl_max_viewport_dims": "[32767, 32767]",
44 | "webgl_unmasked_vendor": "Google Inc. (AMD)",
45 | "webgl_unmasked_renderer": "ANGLE (AMD, AMD Radeon(TM) Graphics (0x00001681) Direct3D11 vs_5_0 ps_5_0, D3D11)",
46 | "webgl_vsf_params": "23,127,127,23,127,127,23,127,127",
47 | "webgl_vsi_params": "0,31,30,0,31,30,0,31,30",
48 | "webgl_fsf_params": "23,127,127,23,127,127,23,127,127",
49 | "webgl_fsi_params": "0,31,30,0,31,30,0,31,30",
50 | "webgl_hash_webgl": "16b93cc1541b205283c1a77320ce4fc0"
51 | }
52 | ]
53 | },
54 | {
55 | "webgl_unmasked_renderer": "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x00009A49) Direct3D11 vs_5_0 ps_5_0, D3D11)",
56 | "webgl": [
57 | {
58 | "webgl_extensions": "ANGLE_instanced_arrays;EXT_blend_minmax;EXT_clip_control;EXT_color_buffer_half_float;EXT_depth_clamp;EXT_disjoint_timer_query;EXT_float_blend;EXT_frag_depth;EXT_polygon_offset_clamp;EXT_shader_texture_lod;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;EXT_texture_mirror_clamp_to_edge;EXT_sRGB;KHR_parallel_shader_compile;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_blend_func_extended;WEBGL_color_buffer_float;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_multi_draw;WEBGL_polygon_mode",
59 | "webgl_extensions_hash": "7300c23f4e6fa34e534fc99c1b628588",
60 | "webgl_renderer": "WebKit WebGL",
61 | "webgl_vendor": "WebKit",
62 | "webgl_version": "WebGL 1.0 (OpenGL ES 2.0 Chromium)",
63 | "webgl_shading_language_version": "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)",
64 | "webgl_aliased_line_width_range": "[1, 1]",
65 | "webgl_aliased_point_size_range": "[1, 1024]",
66 | "webgl_antialiasing": true,
67 | "webgl_bits": "8,8,24,8,8,0",
68 | "webgl_max_params": "16,32,16384,1024,16384,16,16,30,16,16,4096",
69 | "webgl_max_viewport_dims": "[32767, 32767]",
70 | "webgl_unmasked_vendor": "Google Inc. (Intel)",
71 | "webgl_unmasked_renderer": "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x00009A49) Direct3D11 vs_5_0 ps_5_0, D3D11)",
72 | "webgl_vsf_params": "23,127,127,23,127,127,23,127,127",
73 | "webgl_vsi_params": "0,31,30,0,31,30,0,31,30",
74 | "webgl_fsf_params": "23,127,127,23,127,127,23,127,127",
75 | "webgl_fsi_params": "0,31,30,0,31,30,0,31,30",
76 | "webgl_hash_webgl": "d794114e20f8a75cec3b812f98a42b7e"
77 | }
78 | ]
79 | },
80 | {
81 | "webgl_unmasked_renderer": "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x000046A6) Direct3D11 vs_5_0 ps_5_0, D3D11)",
82 | "webgl": [
83 | {
84 | "webgl_extensions": "ANGLE_instanced_arrays;EXT_blend_minmax;EXT_clip_control;EXT_color_buffer_half_float;EXT_depth_clamp;EXT_disjoint_timer_query;EXT_float_blend;EXT_frag_depth;EXT_polygon_offset_clamp;EXT_shader_texture_lod;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;EXT_texture_mirror_clamp_to_edge;EXT_sRGB;KHR_parallel_shader_compile;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_blend_func_extended;WEBGL_color_buffer_float;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_multi_draw;WEBGL_polygon_mode",
85 | "webgl_extensions_hash": "7300c23f4e6fa34e534fc99c1b628588",
86 | "webgl_renderer": "WebKit WebGL",
87 | "webgl_vendor": "WebKit",
88 | "webgl_version": "WebGL 1.0 (OpenGL ES 2.0 Chromium)",
89 | "webgl_shading_language_version": "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)",
90 | "webgl_aliased_line_width_range": "[1, 1]",
91 | "webgl_aliased_point_size_range": "[1, 1024]",
92 | "webgl_antialiasing": true,
93 | "webgl_bits": "8,8,24,8,8,0",
94 | "webgl_max_params": "16,32,16384,1024,16384,16,16,30,16,16,4096",
95 | "webgl_max_viewport_dims": "[32767, 32767]",
96 | "webgl_unmasked_vendor": "Google Inc. (Intel)",
97 | "webgl_unmasked_renderer": "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x000046A6) Direct3D11 vs_5_0 ps_5_0, D3D11)",
98 | "webgl_vsf_params": "23,127,127,23,127,127,23,127,127",
99 | "webgl_vsi_params": "0,31,30,0,31,30,0,31,30",
100 | "webgl_fsf_params": "23,127,127,23,127,127,23,127,127",
101 | "webgl_fsi_params": "0,31,30,0,31,30,0,31,30",
102 | "webgl_hash_webgl": "134cccc7e07355b85e9546597e143227"
103 | }
104 | ]
105 | }
106 | ]
--------------------------------------------------------------------------------