From 5e9113b4c10b677583487b5920306ef80fa145d5 Mon Sep 17 00:00:00 2001 From: zhongfly <11155705+zhongfly@users.noreply.github.com> Date: Mon, 27 Jan 2025 00:40:46 +0800 Subject: [PATCH] =?UTF-8?q?=E7=88=B6=E8=BF=9B=E7=A8=8B=E7=99=BD=E5=90=8D?= =?UTF-8?q?=E5=8D=95=20&=20mac=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 19 ++- go.mod | 3 + main.go | 243 ++++++++++++++++++++----------------- process_unix.go | 20 +++ process_windows.go | 62 ++++++++++ 5 files changed, 229 insertions(+), 118 deletions(-) create mode 100644 go.mod create mode 100644 process_unix.go create mode 100644 process_windows.go diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aedc319..4d2aba3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,6 +13,8 @@ jobs: ext: "" - os: windows-latest ext: ".exe" +# - os: macos-latest +# ext: "" steps: - name: Checkout repository @@ -34,16 +36,25 @@ jobs: - name: Build the application shell: bash run: | - sed -i 's/AppId\s*=\s*".*"/AppId = "${{ secrets.AppId }}"/' main.go - sed -i 's/AppSecret\s*=\s*".*"/AppSecret = "${{ secrets.AppSecret }}"/' main.go - go build -ldflags="-s -w" -o bin/main main.go + if [[ "$RUNNER_OS" == "macOS" ]]; then + sed -i '' 's/AppId\s*=\s*".*"/AppId = "${{ secrets.AppId }}"/' main.go + sed -i '' 's/AppSecret\s*=\s*".*"/AppSecret = "${{ secrets.AppSecret }}"/' main.go + else + sed -i 's/AppId\s*=\s*".*"/AppId = "${{ secrets.AppId }}"/' main.go + sed -i 's/AppSecret\s*=\s*".*"/AppSecret = "${{ secrets.AppSecret }}"/' main.go + fi + go build -ldflags="-s -w" -o bin/main + if [[ "$RUNNER_OS" == "macOS" ]]; then + mv bin/main bin/dandanplay${{ matrix.ext }} + fi rm -f main.go - name: Compress with UPX + if: matrix.os != 'macos-latest' run: upx --best --lzma -o bin/dandanplay${{ matrix.ext }} bin/main - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: dandanplay${{ matrix.ext }} + name: dandanplay-${{ matrix.os }} path: bin/dandanplay${{ matrix.ext }} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..871e0a0 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module dandanplay + +go 1.23.5 diff --git a/main.go b/main.go index 7826799..b5b9d78 100644 --- a/main.go +++ b/main.go @@ -1,147 +1,162 @@ package main import ( - "crypto/sha256" - "encoding/base64" - "flag" - "fmt" - "io" - "net/http" - "net/url" - "os" - "strings" - "time" + "crypto/sha256" + "encoding/base64" + "flag" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strings" + "time" ) // 常量定义 const ( - AppId = "your_app_id" - AppSecret = "your_app_secret" + AppId = "your_app_id" + AppSecret = "your_app_secret" ) // 提取URL中的Path部分 func extractPath(rawURL string) (string, error) { - parsedURL, err := url.Parse(rawURL) - if err != nil { - return "", err - } - return parsedURL.Path, nil + parsedURL, err := url.Parse(rawURL) + if err != nil { + return "", err + } + return parsedURL.Path, nil } // 生成 X-Signature func generateXSignature(rawURL string) (string, int64, error) { - // 获取当前的 Unix 时间戳 - timestamp := time.Now().Unix() + // 获取当前的 Unix 时间戳 + timestamp := time.Now().Unix() - // 提取 URL 的 Path 部分 - urlPath, err := extractPath(rawURL) - if err != nil { - return "", 0, err - } + // 提取 URL 的 Path 部分 + urlPath, err := extractPath(rawURL) + if err != nil { + return "", 0, err + } - // 拼接字符串 - dataToHash := fmt.Sprintf("%s%d%s%s", AppId, timestamp, urlPath, AppSecret) + // 拼接字符串 + dataToHash := fmt.Sprintf("%s%d%s%s", AppId, timestamp, urlPath, AppSecret) - // 计算 SHA256 哈希值 - hash := sha256.Sum256([]byte(dataToHash)) + // 计算 SHA256 哈希值 + hash := sha256.Sum256([]byte(dataToHash)) - // 将哈希值转换为 Base64 编码格式 - base64Hash := base64.StdEncoding.EncodeToString(hash[:]) + // 将哈希值转换为 Base64 编码格式 + base64Hash := base64.StdEncoding.EncodeToString(hash[:]) - return base64Hash, timestamp, nil + return base64Hash, timestamp, nil } // 定义一个类型用于存储多次传递的 -H 参数 type headerFlags []string func (h *headerFlags) String() string { - return fmt.Sprintf("%v", *h) + return fmt.Sprintf("%v", *h) } func (h *headerFlags) Set(value string) error { - *h = append(*h, value) - return nil + *h = append(*h, value) + return nil } func main() { - // 定义命令行参数 - method := flag.String("X", "GET", "HTTP request method") - data := flag.String("d", "", "HTTP POST data") - output := flag.String("o", "", "Output file") - var headers headerFlags - flag.Var(&headers, "H", "HTTP headers") - - // 解析命令行参数 - flag.Parse() - - // 获取URL - args := flag.Args() - if len(args) < 1 { - fmt.Println("Usage: go run main.go [options] ") - return - } - rawURL := args[0] - - // 创建 HTTP 请求 - client := &http.Client{} - req, err := http.NewRequest(*method, rawURL, strings.NewReader(*data)) - if err != nil { - fmt.Printf("Failed to create HTTP request: %v\n", err) - return - } - - // 处理头信息 - for _, header := range headers { - kv := strings.SplitN(header, ":", 2) - if len(kv) == 2 { - req.Header.Add(strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])) - } else { - fmt.Printf("Invalid header format: %v\n", header) - } - } - - // 只在URL是以 https://api.dandanplay.net/ 开头时添加自定义 HTTP 头 - if strings.HasPrefix(rawURL, "https://api.dandanplay.net/") { - // 生成 X-Signature - xSignature, timestamp, err := generateXSignature(rawURL) - if err != nil { - fmt.Printf("Failed to generate X-Signature: %v\n", err) - return - } - - req.Header.Add("X-Signature", xSignature) - req.Header.Add("X-AppId", AppId) - req.Header.Add("X-Timestamp", fmt.Sprintf("%d", timestamp)) - } - - // 发送 HTTP 请求 - resp, err := client.Do(req) - if err != nil { - fmt.Printf("Failed to send HTTP request: %v\n", err) - return - } - defer resp.Body.Close() - - // 输出响应 - var outputWriter io.Writer = os.Stdout - if *output != "" { - file, err := os.Create(*output) - if err != nil { - fmt.Printf("Failed to create output file: %v\n", err) - return - } - defer file.Close() - outputWriter = file - } - - // // 打印响应状态 - // fmt.Fprintln(outputWriter, "Response status:", resp.Status) - // // 打印响应头 - // for key, values := range resp.Header { - // for _, value := range values { - // fmt.Fprintf(outputWriter, "%s: %s\n", key, value) - // } - // } - // 打印响应体 - io.Copy(outputWriter, resp.Body) + // 定义命令行参数 + method := flag.String("X", "GET", "HTTP request method") + data := flag.String("d", "", "HTTP POST data") + output := flag.String("o", "", "Output file") + var headers headerFlags + flag.Var(&headers, "H", "HTTP headers") + + // 解析命令行参数 + flag.Parse() + + // 获取URL + args := flag.Args() + if len(args) < 1 { + fmt.Println("Usage: go run main.go [options] ") + return + } + rawURL := args[0] + + // 检查父进程名称 + parentProcessName, err := getParentProcessName() + if err != nil { + fmt.Printf("Failed to get parent process name: %v\n", err) + return + } + // 创建 HTTP 请求 + client := &http.Client{} + req, err := http.NewRequest(*method, rawURL, strings.NewReader(*data)) + if err != nil { + fmt.Printf("Failed to create HTTP request: %v\n", err) + return + } + + // 处理头信息 + for _, header := range headers { + kv := strings.SplitN(header, ":", 2) + if len(kv) == 2 { + req.Header.Add(strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])) + } else { + fmt.Printf("Invalid header format: %v\n", header) + } + } + + allowed := []string{"mpv", "mpvnet", "implay", "tsukimi", "smplayer", "iina"} + // 判断 parentProcessName 是否在 allowed 中 + isAllowed := false + for _, v := range allowed { + if v == parentProcessName { + isAllowed = true + break + } + } + + // 如果命中 allowed 且链接匹配,则添加相应头 + if isAllowed && strings.HasPrefix(rawURL, "https://api.dandanplay.net/") { + xSignature, timestamp, err := generateXSignature(rawURL) + if err != nil { + fmt.Printf("Failed to generate X-Signature: %v\n", err) + return + } + + req.Header.Add("X-Signature", xSignature) + req.Header.Add("X-AppId", AppId) + req.Header.Add("X-Timestamp", fmt.Sprintf("%d", timestamp)) + } + + // 发送 HTTP 请求 + resp, err := client.Do(req) + if err != nil { + fmt.Printf("Failed to send HTTP request: %v\n", err) + return + } + defer resp.Body.Close() + + // 输出响应 + var outputWriter io.Writer = os.Stdout + if *output != "" { + file, err := os.Create(*output) + if err != nil { + fmt.Printf("Failed to create output file: %v\n", err) + return + } + defer file.Close() + outputWriter = file + } + + // // 打印响应状态 + // fmt.Fprintln(outputWriter, "Response status:", resp.Status) + // // 打印响应头 + // for key, values := range resp.Header { + // for _, value := range values { + // fmt.Fprintf(outputWriter, "%s: %s\n", key, value) + // } + // } + // 打印响应体 + io.Copy(outputWriter, resp.Body) } diff --git a/process_unix.go b/process_unix.go new file mode 100644 index 0000000..bc4e3c9 --- /dev/null +++ b/process_unix.go @@ -0,0 +1,20 @@ +//go:build linux || darwin + +package main + +import ( + "os" + "os/exec" + "strings" +) + +// 在 macOS 上获取父进程名称 +func getParentProcessName() (string, error) { + ppid := os.Getppid() + cmd := exec.Command("ps", "-p", string(ppid), "-o", "comm=") + output, err := cmd.Output() + if err != nil { + return "", err + } + return strings.TrimSpace(string(output)), nil +} diff --git a/process_windows.go b/process_windows.go new file mode 100644 index 0000000..45f7390 --- /dev/null +++ b/process_windows.go @@ -0,0 +1,62 @@ +//go:build windows + +package main + +import ( + "os" + "syscall" + "unsafe" +) + +const processQueryInformation = 0x0400 +const processVmRead = 0x0010 + +// 获取父进程名称 (Windows) +func getParentProcessName() (string, error) { + ppid := os.Getppid() + handle, err := syscall.OpenProcess(processQueryInformation|processVmRead, false, uint32(ppid)) + if err != nil { + return "", err + } + defer syscall.CloseHandle(handle) + + var modName [syscall.MAX_PATH]uint16 + size := uint32(len(modName)) + err = GetModuleBaseName(handle, 0, &modName[0], size) + if err != nil { + return "", err + } + + procName := syscall.UTF16ToString(modName[:]) + if len(procName) > 4 && procName[len(procName)-4:] == ".exe" { + procName = procName[:len(procName)-4] + } + return procName, nil +} + +// GetModuleBaseName is a wrapper around the Windows API function that retrieves the name of the specified module +func GetModuleBaseName(handle syscall.Handle, hModule syscall.Handle, baseName *uint16, size uint32) (err error) { + r1, _, e1 := syscall.Syscall6( + procGetModuleBaseName.Addr(), + 4, + uintptr(handle), + uintptr(hModule), + uintptr(unsafe.Pointer(baseName)), + uintptr(size), + 0, + 0, + ) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +var ( + modpsapi = syscall.NewLazyDLL("psapi.dll") + procGetModuleBaseName = modpsapi.NewProc("GetModuleBaseNameW") +)