mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-21 22:30:46 +08:00
- iOS 虚拟定位工具,基于本地 HTTP 代理 MITM 方案 - MapKit 原生地图体验,支持搜索、收藏、实时定位 - 完整的设置引导流程(证书安装、WiFi 代理配置、环境验证) - 支持 iOS 15+,SwiftUI 构建
171 lines
4.8 KiB
Go
171 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"errors"
|
|
"math/big"
|
|
"net"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// certificateServer exposes a device-specific CA download before the Packet
|
|
// Tunnel starts and a TLS endpoint whose successful default validation proves
|
|
// that iOS has installed and fully trusted that CA.
|
|
type certificateServer struct {
|
|
httpServer *http.Server
|
|
httpsServer *http.Server
|
|
httpLn net.Listener
|
|
httpsLn net.Listener
|
|
leafSHA256 string
|
|
closeOnce sync.Once
|
|
closeErr error
|
|
}
|
|
|
|
func startCertificateServer(caCertPEM, caKeyPEM []byte) (*certificateServer, error) {
|
|
ca, err := parseCA(caCertPEM, caKeyPEM)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ca.Leaf.IsCA {
|
|
return nil, errors.New("certificate server requires a CA certificate")
|
|
}
|
|
|
|
leaf, leafDER, err := issueLoopbackLeaf(ca)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rootDER := ca.Leaf.Raw
|
|
httpLn, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
httpsLn, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
_ = httpLn.Close()
|
|
return nil, err
|
|
}
|
|
|
|
downloadMux := http.NewServeMux()
|
|
downloadMux.HandleFunc("/ca.cer", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/x-x509-ca-cert")
|
|
w.Header().Set("Content-Disposition", `attachment; filename="LocationSpoofer-CA.cer"`)
|
|
_, _ = w.Write(rootDER)
|
|
})
|
|
|
|
probeMux := http.NewServeMux()
|
|
probeMux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
_, _ = w.Write([]byte("ok\n"))
|
|
})
|
|
|
|
server := &certificateServer{
|
|
httpServer: &http.Server{Handler: downloadMux},
|
|
httpsServer: &http.Server{Handler: probeMux},
|
|
httpLn: httpLn,
|
|
httpsLn: httpsLn,
|
|
leafSHA256: sha256Base64(leafDER),
|
|
}
|
|
go func() {
|
|
if err := server.httpServer.Serve(httpLn); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
logEvent("certificate download server error: " + err.Error())
|
|
}
|
|
}()
|
|
go func() {
|
|
tlsListener := tls.NewListener(httpsLn, &tls.Config{Certificates: []tls.Certificate{leaf}, MinVersion: tls.VersionTLS12})
|
|
if err := server.httpsServer.Serve(tlsListener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
logEvent("certificate probe server error: " + err.Error())
|
|
}
|
|
}()
|
|
return server, nil
|
|
}
|
|
|
|
func issueLoopbackLeaf(ca *tls.Certificate) (tls.Certificate, []byte, error) {
|
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
return tls.Certificate{}, nil, err
|
|
}
|
|
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
serial, err := rand.Int(rand.Reader, serialLimit)
|
|
if err != nil {
|
|
return tls.Certificate{}, nil, err
|
|
}
|
|
now := time.Now()
|
|
template := &x509.Certificate{
|
|
SerialNumber: serial,
|
|
Subject: pkix.Name{CommonName: "Location Spoofer Local Trust Probe"},
|
|
NotBefore: now.Add(-time.Hour),
|
|
NotAfter: now.Add(24 * time.Hour),
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
x509.ExtKeyUsageServerAuth,
|
|
},
|
|
DNSNames: []string{"localhost"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
}
|
|
der, err := x509.CreateCertificate(rand.Reader, template, ca.Leaf, &privateKey.PublicKey, ca.PrivateKey)
|
|
if err != nil {
|
|
return tls.Certificate{}, nil, err
|
|
}
|
|
keyDER, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
|
if err != nil {
|
|
return tls.Certificate{}, nil, err
|
|
}
|
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})
|
|
leaf, err := tls.X509KeyPair(certPEM, keyPEM)
|
|
if err != nil {
|
|
return tls.Certificate{}, nil, err
|
|
}
|
|
leaf.Leaf, err = x509.ParseCertificate(der)
|
|
if err != nil {
|
|
return tls.Certificate{}, nil, err
|
|
}
|
|
return leaf, der, nil
|
|
}
|
|
|
|
func sha256Base64(data []byte) string {
|
|
sum := sha256.Sum256(data)
|
|
return base64.StdEncoding.EncodeToString(sum[:])
|
|
}
|
|
|
|
func (s *certificateServer) DownloadURL() string {
|
|
return "http://" + s.httpLn.Addr().String() + "/ca.cer"
|
|
}
|
|
|
|
func (s *certificateServer) ProbeURL() string {
|
|
return "https://" + s.httpsLn.Addr().String() + "/health"
|
|
}
|
|
|
|
func (s *certificateServer) LeafSHA256() string { return s.leafSHA256 }
|
|
|
|
func (s *certificateServer) Close() error {
|
|
s.closeOnce.Do(func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
if err := s.httpServer.Shutdown(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
s.closeErr = err
|
|
}
|
|
if err := s.httpsServer.Shutdown(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) && s.closeErr == nil {
|
|
s.closeErr = err
|
|
}
|
|
})
|
|
return s.closeErr
|
|
}
|