文章

联通光猫重启脚本 Golang

使用Dify尝试完成重启

到登录成功这一步放弃了,确实没有自己写代码快,而且需要把路由映射到公网,-_-||

编译

GOOS=linux GOARCH=mipsle GOMIPS=softfloat CGO_ENABLED=0 go build

代码如下

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	mrand "math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	userAgent       = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
	acceptHeader    = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
	acceptLanguage  = "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"
	cacheControl    = "no-cache"
	connection      = "keep-alive"
	pragma          = "no-cache"
	upgradeRequests = "1"
)

var (
	baseURL  = "http://192.168.1.1/"
	password = "123456"
)

func main() {
	if len(os.Args) > 1 {
		baseURL = os.Args[1]
	}
	if len(os.Args) > 2 {
		password = os.Args[2]
	}
	tokens, _ := requestPageLoginToken()
	fmt.Println("获取到的token为:", tokens)
	cookies := doLoginAndGetCookies(tokens)
	fmt.Println("获取到的cookies为:", cookies)
	pubKey := getPublicKey(cookies)
	fmt.Println("获取到的公钥为:", pubKey)
	restartLightCat(pubKey, cookies)
	fmt.Println("重启成功!")
}

func doLoginAndGetCookies(tokens []string) string {
	loginInfo := prepareLoginInfo(tokens)

	client := &http.Client{}
	req := createRequest("POST", baseURL, loginInfo, tokens[2])
	resp, _ := client.Do(req)
	defer resp.Body.Close()
	return strings.Join(resp.Header.Values("Set-Cookie"), "")
}

func prepareLoginInfo(cookies []string) map[string]string {
	loginInfo := map[string]string{
		"Frm_Logintoken":      cookies[0],
		"Frm_Loginchecktoken": cookies[1],
		"_cu_url":             "0",
		"Right":               "2",
		"Username":            "",
		"Password":            password,
		"action":              "login",
		"accountType":         "",
	}

	mrand.Seed(time.Now().UnixNano())
	randomNum := mrand.Intn(89999999) + 10000000
	loginInfo["UserRandomNum"] = fmt.Sprintf("%d", randomNum)

	sha256Sum := sha256.Sum256([]byte(loginInfo["Password"] + loginInfo["UserRandomNum"]))
	loginInfo["Password"] = hex.EncodeToString(sha256Sum[:])

	return loginInfo
}

func restartLightCat(pubKey, cookies string) {
	sessionToken := getSessionToken(cookies)

	data := "IF_ACTION=devrestart&IF_ERRORSTR=SUCC&IF_ERRORPARAM=SUCC&IF_ERRORTYPE=-1&flag=1&_SESSION_TOKEN=" + sessionToken
	key := "1579533123347452"
	iv := "5616861466389323"
	encryptedKey, err := EncodeKey(pubKey, key, iv)
	encryptedData, err := EncodePara(data, key, iv)
	payloadMap := map[string]string{
		"IF_ENCODE":      encryptedKey,
		"IF_ENCODEPARAM": encryptedData,
	}
	client := &http.Client{}
	request := createRequest("POST", baseURL+"getpage.gch?pid=1002&nextpage=manager_dev_restart_t_BJ.gch", payloadMap, cookies)
	request.Header.Add("referer", baseURL+"getpage.gch?pid=1002&nextpage=manager_dev_restart_t_BJ.gch")
	resp, err := client.Do(request)
	if err != nil {
		fmt.Println("发送请求失败:", err)
		return
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)

	if err != nil {
		fmt.Println("读取响应内容失败:", err)
		return
	}

	if strings.Contains(string(body), "Transfer_meaning('flag','1')") {
		fmt.Println("重启成功,请等待完成!")
	}
}

func getSessionToken(cookies string) string {

	url := baseURL + "getpage.gch?pid=1002&nextpage=manager_dev_restart_t_BJ.gch"
	client := &http.Client{}
	req := createRequest("GET", url, nil, cookies)
	req.Header.Add("referer", baseURL+"/top.gch")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("发送请求失败:", err)
		return ""
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取响应内容失败:", err)
		return ""
	}

	html := string(body)
	regex := regexp.MustCompile(`var\s+session_token\s+=\s+"(\w+)";`)
	result := regex.FindStringSubmatch(html)

	if len(result) > 1 {
		sessionToken := result[1]
		fmt.Println("获得 SessionToken " + sessionToken)
		return sessionToken
	}

	fmt.Println("未找到 SessionToken")
	return ""
}

func createRequest(method, baseUrl string, data map[string]string, cookie string) *http.Request {
	formData := url.Values{}
	for key, value := range data {
		formData.Add(key, value)
	}

	req, err := http.NewRequest(method, baseUrl, strings.NewReader(formData.Encode()))
	if err != nil {
		fmt.Println("创建请求失败:", err)
		return nil
	}

	setCommonHeaders(req)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Origin", baseURL)
	req.Header.Add("Referer", baseURL)
	req.Header.Add("Cookie", cookie)

	return req
}

func setCommonHeaders(req *http.Request) {
	req.Header.Add("Accept", acceptHeader)
	req.Header.Add("Accept-Language", acceptLanguage)
	req.Header.Add("Cache-Control", cacheControl)
	req.Header.Add("Connection", connection)
	req.Header.Add("Pragma", pragma)
	req.Header.Add("Upgrade-Insecure-Requests", upgradeRequests)
	req.Header.Add("User-Agent", userAgent)
}

func requestPageLoginToken() ([]string, error) {
	resp, _ := http.Get(baseURL)

	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	tokenRegex := regexp.MustCompile(`document\.getElementById\("Frm_Logintoken"\)\.value = "(\d+)"`)
	checkTokenRegex := regexp.MustCompile(`document\.getElementById\("Frm_Loginchecktoken"\)\.value = "([^"]+)"`)

	token := tokenRegex.FindStringSubmatch(string(body))
	checkToken := checkTokenRegex.FindStringSubmatch(string(body))

	setCookies := resp.Header.Values("Set-Cookie")

	result := make([]string, 0)
	if len(token) > 1 {
		result = append(result, token[1])
	} else {
		result = append(result, "未找到 Frm_Logintoken")
	}

	if len(checkToken) > 1 {
		result = append(result, checkToken[1])
	} else {
		result = append(result, "未找到 Frm_Loginchecktoken")
	}

	for _, cookie := range setCookies {
		result = append(result, cookie)
	}

	return result, nil
}

func getPublicKey(cookies string) string {

	client := &http.Client{}
	request := createRequest("GET", baseURL+"js/code.js", nil, cookies)
	request.Header.Add("referer", baseURL+"/unicom.html")
	resp, _ := client.Do(request)

	// 读取响应体
	body, _ := ioutil.ReadAll(resp.Body)

	// 定义正则表达式来匹配pubKey
	re := regexp.MustCompile(`-----BEGIN PUBLIC KEY-----[\s\S]*?-----END PUBLIC KEY-----`)

	// 查找匹配的内容
	match := re.FindString(string(body))
	match = strings.Replace(match, `\n\`, "", -1)
	return match
}

// EncodeKey 使用RSA公钥加密key和iv
func EncodeKey(pubKey, key, iv string) (string, error) {
	// 解析RSA公钥
	block, _ := pem.Decode([]byte(pubKey))
	if block == nil || block.Type != "PUBLIC KEY" {
		return "", errors.New("failed to decode PEM block containing public key")
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse public key: %v", err)
	}

	rsaPub, ok := pub.(*rsa.PublicKey)
	if !ok {
		return "", errors.New("not an RSA public key")
	}

	// 加密key和iv
	str := key + "+" + iv
	encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPub, []byte(str))
	if err != nil {
		return "", fmt.Errorf("failed to encrypt: %v", err)
	}

	return base64.StdEncoding.EncodeToString(encrypted), nil
}

// EncodePara 使用AES加密数据
func EncodePara(data, key, iv string) (string, error) {
	// 生成AES密钥和IV
	keyHash := sha256.Sum256([]byte(key))
	ivHash := sha256.Sum256([]byte(iv))

	block, err := aes.NewCipher(keyHash[:])
	if err != nil {
		return "", fmt.Errorf("failed to create AES cipher: %v", err)
	}

	// 填充数据
	paddedData := zeroPadding([]byte(data), aes.BlockSize)

	// 加密
	ciphertext := make([]byte, len(paddedData))
	mode := cipher.NewCBCEncrypter(block, ivHash[:aes.BlockSize])
	mode.CryptBlocks(ciphertext, paddedData)

	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

// zeroPadding 填充数据
func zeroPadding(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padtext := make([]byte, padding)
	return append(data, padtext...)
}