Last active
June 4, 2025 06:35
-
-
Save jyxjjj/a5c964c677e41bb37899129a1a3276a4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/sha256" | |
"crypto/tls" | |
"encoding/hex" | |
"fmt" | |
"io" | |
"log" | |
"net" | |
"net/http" | |
"os" | |
"sort" | |
"strings" | |
) | |
func main() { | |
if len(os.Args) != 2 { | |
fmt.Fprintln(os.Stderr, "用法: ./程序名 证书路径") | |
os.Exit(1) | |
} | |
certPath := os.Args[1] | |
cert, err := tls.LoadX509KeyPair(certPath, certPath) | |
if err != nil { | |
log.Fatalf("加载证书失败: %v", err) | |
} | |
h := func(w http.ResponseWriter, r *http.Request) { | |
io.Copy(w, r.Body) | |
} | |
server := &http.Server{ | |
Addr: ":8444", | |
Handler: http.HandlerFunc(h), | |
TLSConfig: &tls.Config{ | |
Certificates: []tls.Certificate{cert}, | |
GetCertificate: func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) { | |
processClientHello(chi) | |
return &cert, nil | |
}, | |
}, | |
} | |
log.Println("Starting HTTPS echo server on :8444 ...") | |
err = server.ListenAndServeTLS(certPath, certPath) | |
if err != nil { | |
log.Fatalf("监听失败: %v", err) | |
} | |
} | |
func processClientHello(chi *tls.ClientHelloInfo) { | |
// 协议类型 | |
proto := "t" | |
// TLS 版本 | |
version := "00" | |
if len(chi.SupportedVersions) > 0 { | |
versionMap := map[uint16]string{ | |
tls.VersionTLS10: "10", | |
tls.VersionTLS11: "11", | |
tls.VersionTLS12: "12", | |
tls.VersionTLS13: "13", | |
} | |
if v, ok := versionMap[chi.SupportedVersions[len(chi.SupportedVersions)-1]]; ok { | |
version = v | |
} | |
} | |
log.Printf("TLS version: %s", version) | |
// SNI 类型 | |
sniType := "i" | |
if chi.ServerName != "" && net.ParseIP(chi.ServerName) == nil { | |
sniType = "d" | |
} | |
// 密码套件 | |
cipherHex := []string{} | |
cipherCount := "00" | |
if len(chi.CipherSuites) > 0 { | |
ciphers := filterGREASE(chi.CipherSuites) | |
cipherHex = toHexList(ciphers) | |
cipherCount = fmt.Sprintf("%02d", min(len(cipherHex), 99)) | |
} | |
// 扩展 | |
extCount := "00" | |
exts := []string{} | |
if len(chi.SupportedProtos) > 0 { | |
exts = chi.SupportedProtos | |
extCount = fmt.Sprintf("%02d", min(len(exts), 99)) | |
} | |
// ALPN | |
alpn := "00" | |
if len(chi.SupportedProtos) > 0 { | |
alpn = formatALPN(chi.SupportedProtos[0]) | |
} | |
// 密码套件哈希 | |
cipherHash := hashList(cipherHex) | |
// 扩展哈希 | |
extHash := hashList(exts) | |
// 输出 JA4 字段时加下划线分隔,且保证每个字段都输出 | |
ja4 := fmt.Sprintf("%s%s%s%s%s%s_%s_%s", proto, version, sniType, cipherCount, extCount, alpn, cipherHash, extHash) | |
log.Printf("JA4: %s", ja4) | |
} | |
func filterGREASE(list []uint16) []uint16 { | |
var result []uint16 | |
for _, v := range list { | |
if v&0x0f0f == 0x0a0a { | |
continue | |
} | |
result = append(result, v) | |
} | |
return result | |
} | |
func toHexList(list []uint16) []string { | |
var hexList []string | |
for _, v := range list { | |
hexList = append(hexList, fmt.Sprintf("%04x", v)) | |
} | |
sort.Strings(hexList) | |
return hexList | |
} | |
func formatALPN(s string) string { | |
if len(s) == 0 { | |
return "00" | |
} | |
first, last := s[0], s[len(s)-1] | |
if !isAlnum(first) || !isAlnum(last) { | |
return fmt.Sprintf("%x%x", first, last) | |
} | |
return fmt.Sprintf("%c%c", first, last) | |
} | |
func isAlnum(b byte) bool { | |
return (b >= '0' && b <= '9') || (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') | |
} | |
func hashList(list []string) string { | |
if len(list) == 0 { | |
return "000000000000" | |
} | |
joined := strings.Join(list, ",") | |
sum := sha256.Sum256([]byte(joined)) | |
return hex.EncodeToString(sum[:])[:12] | |
} | |
func min(a, b int) int { | |
if a < b { | |
return a | |
} | |
return b | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
{ | |
$method = match ($request->method()) { | |
'GET' => 'ge', | |
'POST' => 'po', | |
'PUT' => 'pu', | |
'DELETE' => 'de', | |
'PATCH' => 'pa', | |
'HEAD' => 'he', | |
'OPTIONS' => 'op', | |
default => '00', | |
}; | |
$version = $request->server('SERVER_PROTOCOL') === 'HTTP/2.0' ? 20 : 11; | |
$c = count($request->cookies->all()) > 0 ? 'c' : 'n'; | |
$r = $request->headers->get('Referer', 'n'); | |
$headers = $request->headers->all(); | |
$headers = array_filter($headers, function ($key) { | |
$header = strtolower($key); | |
if (str_starts_with($header, 'x-')) { | |
return false; | |
} | |
if (str_starts_with($header, 'cf-')) { | |
return false; | |
} | |
if (str_starts_with($header, 'auth')) { | |
return false; | |
} | |
if ($header === 'cookie') { | |
return false; | |
} | |
if ($header === 'referer') { | |
return false; | |
} | |
return true; | |
}, ARRAY_FILTER_USE_KEY); | |
$num = count($headers); | |
$num = str_pad($num, 2, '0', STR_PAD_LEFT); | |
if ($num > 99) { | |
$num = 99; | |
} | |
$lang = strtolower(substr(str_replace('-', '', $request->header('Accept-Language', '0000')), 0, 4)); | |
$a = "$method$version$c$r$num$lang"; | |
$headersStr = ''; | |
foreach ($headers as $key => $value) { | |
$headersStr .= sprintf("%s: %s\n", $key, implode('; ', (array)$value)); | |
} | |
$headersHash = substr(hash('sha256', $headersStr), 0, 12); | |
$cookies = $request->cookies->all(); | |
$cookiesStr1 = ''; | |
$cookiesStr2 = ''; | |
ksort($cookies); | |
foreach ($cookies as $key => $value) { | |
$cookiesStr1 .= "$key\n"; | |
$cookiesStr2 .= "$key=$value\n"; | |
} | |
$cookiesHash1 = substr(hash('sha256', $cookiesStr1), 0, 12); | |
$cookiesHash2 = substr(hash('sha256', $cookiesStr2), 0, 12); | |
$result = "{$a}_{$headersHash}_{$cookiesHash1}_{$cookiesHash2}"; | |
return $this->success([ | |
'type' => 'ja4h', | |
'fp' => $result, | |
'fph' => hash('sha256', $result), | |
]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment