├── .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 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | wakatime 4 |
5 | showcase 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 | 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 | ] --------------------------------------------------------------------------------