wanxu 发表于 2024-1-29 13:40

某视频解析网址加密接口分析

本帖最后由 wanxu 于 2024-1-29 13:54 编辑

之前分享过一次帖子,是扣JS的方法来请求接口
希望各位食客给个关注,加个评论来点更新的动力

这次直接使用C#实现了他的算法,然后在分享一下他的接口时怎么请求以及解密的
用到的工具就是Fiddler和浏览器控制台,然后写了一个C#版本的软件(随手写的有BUG哈哈)
https://static.52pojie.cn/static/image/hrline/line7.png正文开始https://static.52pojie.cn/static/image/hrline/line7.png
地址是:aHR0cHM6Ly9qeC54bWZsdi5jb20v
首先打开FD,然后打开控制台抓一下包,挨个分析

这里有这样一个请求,他的请求参数有我们想看的视频并且响应是加密的

我们先分析他的请求,可以看到有个key是加密的,去控制台打一个XHR断点

可以看到成功断住,我们根据这个断点往上找加密的地方

往上找到这里的时候可以看到key=lllIIlIl,lllIIlIl=sign
先分析sign中的几个参数与结果
I111ill=时间戳,url=编码后的想看的视频的地址

可以看到时间戳拼接视频地址,然后取MD5
这个时候我们去sign中看他的逻辑即可
鼠标悬停进VM虚拟机里看sign代码


是一个AES加密的,然后传过来MD5的结果
就是KEY参数的值
这个时候可以模拟请求拿到对应的响应
结果中有两个参数是加密的,分别是url和html
我们看到同时他的响应中有一个AES_KEY和AES_IV
大胆设想他是AES加密并且同时返回key和iv


一个是url,一个是html,
这个url会返回真实视频地址,两种情况一个是301,一个是直接的视频地址
html是全集和剧的详细信息
C#的代码贴在下方,diamagnetic比较烂只是能用,没有具体优化,各位佬可以根据自己需求在优化
现在他有时候接口返回的响应有一些剧的他自己网站也播放不出来,一直卡着转圈,所以软件也没办法下载,具体哪些能下哪些不能下需要自己测试了


using Newtonsoft.Json;
using System;
using System.IO;
using System.IO.Pipes;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using static System.Net.Mime.MediaTypeNames;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace xmflv
{
    internal class Program
    {
      static async Task Main(string[] args)
      {
            Console.WriteLine("Hello, World!");


            Console.Write("请输入视频URL(支持腾讯视频,爱奇艺):");
            string fromUrl = Console.ReadLine();
            if (string.IsNullOrEmpty(fromUrl))
            {
                Console.WriteLine("输入url为空!");
                Console.ReadLine();
            }

            fromUrl = EncodeUrl(fromUrl);
            //Console.WriteLine(fromUrl);
            //时间戳
            long timeStamp = GenerateTimestamp();
            //Console.WriteLine(timeStamp);
            //时间戳+URL的MD5
            string plaintext = CalculateMD5Hash($"{timeStamp}{UrlDecode(fromUrl)}");
            //Console.WriteLine(plaintext);
            //请求中的key参数
            string ciphertext = Sign(plaintext);
            //Console.WriteLine(ciphertext);
            HttpHelper http = new HttpHelper();
            HttpItem item = new HttpItem()
            {
                URL = "接口地址的url",
                Method = "POST",
                Accept = "application/json, text/javascript, */*; q=0.01",
                ContentType = "application/x-www-form-urlencoded; charset=UTF-8",
                UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
                Postdata = $"wap=1&url={fromUrl}&time={timeStamp}&key={EncodeKey(ciphertext)}",
                //ProxyIp = "127.0.0.1:8888"
            };
            item.Header.Add("Origin", "地址自己抓一下");
            HttpResult result = http.GetHtml(item);
            if (result.StatusCode != System.Net.HttpStatusCode.OK)
            {
                Console.Write("请求失败!");
            }
            else
            {
                string html = result.Html;
                //Console.WriteLine(html);
                var resultDynamic = JsonConvert.DeserializeObject<dynamic>(html);
                if (!string.IsNullOrEmpty(html) && (int)resultDynamic.code == 200)
                {
                  //解密后的视频地址集合
                  var extm3u = DecryptAES((string)resultDynamic.url, (string)resultDynamic.aes_key, (string)resultDynamic.aes_iv);
                  //Console.WriteLine(extm3u);

                  //取得全集链接
                  var AllUrl = ExtractURLs(DecryptAES((string)resultDynamic.html, (string)resultDynamic.aes_key, (string)resultDynamic.aes_iv));

                  item = new HttpItem()
                  {
                        URL = RemoveSpecialCharacters(extm3u),
                        Method = "GET",
                        UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
                        Accept = "*/*",
                        //ProxyIp = "127.0.0.1:8888"
                  };
                  item.Header.Add("Origin", "地址自己抓一下");
                  result = http.GetHtml(item);
                  if (result.StatusCode == System.Net.HttpStatusCode.OK && !string.IsNullOrEmpty(result.Html))
                  {
                        html = result.Html;
                        //Console.WriteLine(html);
                        string[] lines = html.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
                        string folderPath = AppDomain.CurrentDomain.BaseDirectory + $"{resultDynamic.name}";
                        int i = 0;
                        foreach (string line in lines)
                        {
                            if (!line.StartsWith("#"))
                            {

                              //文件夹不存在则创建文件夹
                              if (!Directory.Exists(folderPath))
                              {
                                    Directory.CreateDirectory(folderPath);
                              }

                              string fileName = $"{i}.ts";
                              string filePath = Path.Combine(folderPath, fileName);

                              if (!File.Exists(filePath))
                              {
                                    bool bl = true;
                                    int j = 0;
                                    while (bl)
                                    {
                                        try
                                        {
                                          //WebProxy webProxy = new WebProxy("127.0.0.1", 8888);
                                          using (HttpClient client = new HttpClient())
                                          {
                                                if (extm3u.Contains("IP地址包含就用这个"))
                                                {
                                                    //先获取重定向的url
                                                    item = new HttpItem()
                                                    {
                                                      URL = "https://服务器地址自己抓一下/" + line,
                                                      Method = "GET",
                                                      UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
                                                      Accept = "*/*",
                                                      //ProxyIp = "127.0.0.1:8888"
                                                    };
                                                    item.Header.Add("Origin", "地址自己抓一下");
                                                    result = http.GetHtml(item);
                                                    var location = result.RedirectUrl;

                                                    using (HttpResponseMessage response = await client.GetAsync(location))
                                                    {
                                                      response.EnsureSuccessStatusCode();

                                                      using (Stream stream = await response.Content.ReadAsStreamAsync())
                                                      {
                                                            using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                                                            {
                                                                await stream.CopyToAsync(fileStream); // 将响应流保存到本地文件
                                                                bl = false;
                                                                break;
                                                            }
                                                      }
                                                    }
                                                }
                                                else
                                                {
                                                    //client.Timeout = TimeSpan.FromSeconds(120); // 设置超时时间为 120 秒
                                                    //HttpClient.DefaultProxy = webProxy;
                                                    //client.DefaultRequestHeaders.Add("origin", "地址自己抓一下");
                                                    string pattern = @"/([^/]+)$";
                                                    string match = Regex.Replace(extm3u, pattern, "");
                                                    string mp4Url = match + "/" + line;

                                                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(mp4Url);
                                                    request.Method = "GET";
                                                    request.Headers.Add("sec-ch-ua", "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Microsoft Edge\";v=\"120\"");
                                                    request.Headers.Add("sec-ch-ua-mobile", "?0");
                                                    request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0";
                                                    request.Headers.Add("sec-ch-ua-platform", "\"Windows\"");
                                                    request.Accept = "/";
                                                    request.Headers.Add("Origin", "地址自己抓一下");
                                                    request.Headers.Add("Sec-Fetch-Site", "cross-site");
                                                    request.Headers.Add("Sec-Fetch-Mode", "cors");
                                                    request.Headers.Add("Sec-Fetch-Dest", "empty");
                                                    request.Headers.Add("Accept-Encoding", "gzip, deflate, br");
                                                    request.Headers.Add("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
                                                    try
                                                    {
                                                      // Send the request and get the response
                                                      HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                                                      // Read the response content
                                                      using (var stream = response.GetResponseStream())
                                                      {
                                                            using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                                                            {
                                                                await stream.CopyToAsync(fileStream); // 将响应流保存到本地文件
                                                                bl = false;
                                                            }

                                                            response.Close();
                                                            break;
                                                      }
                                                    }
                                                    catch (WebException ex)
                                                    {
                                                      Console.WriteLine("An error occurred: " + ex.Message);
                                                    }

                                                    //using (HttpResponseMessage response = await client.GetAsync(mp4Url))
                                                    //{
                                                    //    if (response.StatusCode == HttpStatusCode.OK)
                                                    //    {

                                                    //      using (Stream stream = await response.Content.ReadAsStreamAsync())
                                                    //      {
                                                    //            using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                                                    //            {
                                                    //                await stream.CopyToAsync(fileStream); // 将响应流保存到本地文件
                                                    //                bl = false;
                                                    //                break;
                                                    //            }
                                                    //      }
                                                    //    }
                                                    //}


                                                }
                                          }
                                        }
                                        catch (Exception ex)
                                        {
                                          j++;
                                          if (j > 3)
                                          {
                                                bl = false;
                                                Console.WriteLine($"3次请求地址自己抓一下{line}失败已经跳过!");
                                          }
                                        }
                                    }


                              }
                              i++;
                            }
                        }

                        CombineMP3(folderPath);
                  }
                  else
                  {
                        Console.WriteLine("请求加密接口失败!");
                  }
                }
                else
                {
                  Console.WriteLine("请求失败!" + html);
                }

            }

            Console.ReadLine();
      }

      static void CombineMP3(string folder)
      {
            string outputFilePath = $"{folder}.mp4";
            string[] mp3Files = Directory.GetFiles(folder, "*.ts"); // 获取文件夹中的所有MP3文件路径

            List<byte[]> mp3DataList = new List<byte[]>();

            List<string> sortedFiles = mp3Files.OrderBy(f => Path.GetFileNameWithoutExtension(f), new NumericFileNameComparer()).ToList();
            foreach (string filePath in sortedFiles)
            {
                byte[] mp3Data = File.ReadAllBytes(filePath); // 读取MP3文件的字节数据
                mp3DataList.Add(mp3Data);
            }

            byte[] outputData = mp3DataList.SelectMany(data => data).ToArray(); // 将所有MP3文件的字节数据合并为一个字节数组

            File.WriteAllBytes(outputFilePath, outputData); // 将字节数组写入输出文件
            Directory.Delete(folder, true);
            Console.WriteLine("合并完成,输出文件路径:" + outputFilePath);
      }


      public static string RemoveSpecialCharacters(string url)
      {
            string pattern = @"[^a-zA-Z0-9.:/?=_-]"; // 匹配非法字符的正则表达式

            return Regex.Replace(url, pattern, ""); // 去掉非法字符
      }
      public class NumericFileNameComparer : IComparer<string>
      {
            public int Compare(string x, string y)
            {
                string fileNameX = Path.GetFileNameWithoutExtension(x);
                string fileNameY = Path.GetFileNameWithoutExtension(y);

                if (fileNameX == null || fileNameY == null)
                {
                  return 0; // 如果文件名为空,则认为两个文件名相等
                }

                int numericValueX, numericValueY;
                bool successX = int.TryParse(fileNameX, out numericValueX);
                bool successY = int.TryParse(fileNameY, out numericValueY);

                if (successX && successY)
                {
                  return numericValueX.CompareTo(numericValueY);
                }
                else if (successX)
                {
                  return -1; // 如果只有 x 是数字,那么 x 小于 y
                }
                else if (successY)
                {
                  return 1; // 如果只有 y 是数字,那么 x 大于 y
                }
                else
                {
                  return string.Compare(fileNameX, fileNameY, StringComparison.Ordinal); // 如果都不是数字,则按照字符串顺序排序
                }
            }
      }


      /// <summary>
      /// 获取全集链接
      /// </summary>
      /// <param name="text"></param>
      /// <returns></returns>
      public static List<string> ExtractURLs(string text)
      {
            List<string> urls = new List<string>();
            Regex regex = new Regex(@"./\?url=(.*?)'");

            MatchCollection matches = regex.Matches(text);
            foreach (Match match in matches)
            {
                string url = match.Groups.Value;
                urls.Add(url);
            }

            return urls;
      }
      /// <summary>
      /// 获取请求中的key
      /// </summary>
      /// <param name="a"></param>
      /// <returns></returns>
      public static string Sign(string a)
      {
            // 计算MD5
            byte[] md5Bytes;
            using (var md5 = MD5.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(a);
                md5Bytes = md5.ComputeHash(inputBytes);
            }
            string b = BitConverter.ToString(md5Bytes).Replace("-", "").ToLower();

            // 使用MD5作为密钥
            byte[] key = Encoding.UTF8.GetBytes(b);

            // 设置IV和Padding
            byte[] iv = Encoding.UTF8.GetBytes("3cccf88181408f19");
            PaddingMode padding = PaddingMode.Zeros;

            // 加密
            byte[] encryptedBytes;
            using (var aes = new AesCryptoServiceProvider())
            {
                aes.Key = key;
                aes.IV = iv;
                aes.Padding = padding;
                aes.Mode = CipherMode.CBC;

                byte[] inputBytes = Encoding.UTF8.GetBytes(a);
                using (var encryptor = aes.CreateEncryptor())
                {
                  encryptedBytes = encryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
                }
            }

            string e = Convert.ToBase64String(encryptedBytes);
            return e;
      }
      /// <summary>
      /// 时间戳+url
      /// </summary>
      /// <param name="input"></param>
      /// <returns></returns>
      static string CalculateMD5Hash(string input)
      {
            using (MD5 md5 = MD5.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);

                StringBuilder stringBuilder = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                  stringBuilder.Append(hashBytes.ToString("x2"));
                }

                return stringBuilder.ToString();
            }
      }
      /// <summary>
      /// 获取时间戳
      /// </summary>
      /// <returns></returns>
      static long GenerateTimestamp()
      {
            DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            TimeSpan timeSpan = DateTime.UtcNow - epoch;
            long timestamp = (long)timeSpan.TotalMilliseconds;
            return timestamp;
      }
      /// <summary>
      /// 对网址进行url编码
      /// </summary>
      /// <param name="url"></param>
      /// <returns></returns>
      static string EncodeUrl(string str)
      {
            string encodedUrl = Uri.EscapeDataString(str);
            encodedUrl = encodedUrl.Replace("%3A", "%253A").Replace("%2F", "%252F");
            return encodedUrl;
      }
      static string EncodeKey(string str)
      {
            string encodedUrl = Uri.EscapeDataString(str);
            return encodedUrl;
      }
      public static string UrlDecode(string encodedUrl)
      {
            string decodedUrl = HttpUtility.UrlDecode(encodedUrl);
            return decodedUrl;
      }
      /// <summary>
      /// 解密
      /// </summary>
      /// <param name="encryptedText"></param>
      /// <param name="key"></param>
      /// <param name="iv"></param>
      /// <returns></returns>
      static string DecryptAES(string encryptedText, string key, string iv)
      {
            byte[] keyBytes = Encoding.UTF8.GetBytes(key);
            byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
            byte[] encryptedBytes = Convert.FromBase64String(encryptedText);

            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = keyBytes;
                aesAlg.IV = ivBytes;
                aesAlg.Padding = PaddingMode.Zeros;
                aesAlg.Mode = CipherMode.CBC;

                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                using (MemoryStream msDecrypt = new MemoryStream(encryptedBytes))
                {
                  using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                  {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            return srDecrypt.ReadToEnd();
                        }
                  }
                }
            }
      }
    }
}



sinmu 发表于 2024-1-29 17:09

我知道怎么弄 。感谢

wanxu 发表于 2024-1-29 17:04

sinmu 发表于 2024-1-29 17:00
大佬,请教一下“去控制台打一个XHR断点” 这个怎么操作,开发者工具可以直接捕获所有的xhr请求和定位对应 ...

搜一搜怎么打XHR断点吧,F12-源代码-右边侧边栏里有一个xhr断点,添加网址就可以

Coldandcolder 发表于 2024-1-29 15:39

还得是大佬们啊,支持!

jueseman 发表于 2024-1-29 13:55

{:1_921:}虽然看不懂,还是支持

slslsl 发表于 2024-1-29 14:15

其实按照原来js的情况来看,如果是混淆不严重的话,应该可以把其中几个比较关键的function给提取出来,然后用C#也好或者其他语言也好,应该大多都有提供对应调用执行js的方法,去调用来计算加密值即可

cuiqiqi123 发表于 2024-1-29 14:20

虽然看不懂,还是支持

aaron505 发表于 2024-1-29 14:52

大佬牛的,感谢分享经验学习

wind315_ 发表于 2024-1-29 14:54

基础知识太多,看不懂,只能纯支持

aimzhangyuting 发表于 2024-1-29 15:10

感谢楼主分享 好人长命百岁

skyrayecfp杰 发表于 2024-1-29 15:23

我在想,我们的硬件加密是不是就是这样破解掉的,被人拿着到处盈利。

mtcf 发表于 2024-1-29 15:26

强中还有强中手啊,果然是牛人
http://aijuanpi.com/Photos/qoute/givethumbsup.jpg
页: [1] 2 3 4 5 6 7
查看完整版本: 某视频解析网址加密接口分析