最近想在douy直播游戏,发现下载了douy直播伴侣没有OBS第三方推流选项,然后去哔哩哔哩等网站搜索教程,基本上都是用他们自己自制的软件进行获取推流密钥等,发现还收费,对于白嫖来说非常不友好。于是只能自己动手了!

需要的工具:

1.wireshark
2.visual studio
3.SharpPcap 4.2.0

视频教程:
B站视频教程

开始实操:
1.使用wireshark分析douy直播伴侣,通过过滤rtmpt协议,可以看到直播地址和推流密钥
16794c18a9e0e409

2.接下来直接上代码!

using MaterialSkin;
using MaterialSkin.Controls;
using PacketDotNet;
using PacketDotNet.Utils;
using SharpPcap;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : MaterialForm
    {
        public static Form1 mainFrm;

        public CaptureDeviceList devices = CaptureDeviceList.Instance;
        public static ICaptureDevice device;
        public delegate void InvokeDelegate(string d1, string d2);
        public static int comboBoxType;
        public static string appVersion = "1.1";
        public static string version;
        /// <summary>
        /// 初始化
        /// </summary>
        public Form1()
        {

            try
            {


                string url = "http://tools.arma3bbs.com/app/douyintools-version.txt";
                version = HttpUtil.GetHttpResponse(url);

                if (!version.Equals(appVersion))
                {
                    MessageBox.Show("版本过低!将为您打开htts://it.o.ls 手动下载 最新版本:"+ version +" 您的版本:"+ appVersion, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                    System.Diagnostics.Process.Start("https://it.o.ls/index.php?/entry/8-%E8%87%AA%E5%88%B6%E8%8E%B7%E5%8F%96douy%E7%9B%B4%E6%92%AD%E6%8E%A8%E6%B5%81%E5%AF%86%E9%92%A5%E8%BD%AF%E4%BB%B6%EF%BC%8C%E7%9C%81%E9%92%B1%E4%B9%B0%E7%82%B9%E5%90%83%E7%9A%84%E4%B8%8D%E9%A6%99%E5%98%9B/");
                    System.Environment.Exit(0);

                }
            }
            catch (Exception e)
            {
                
                MessageBox.Show("版本检测失败!请访问htts://it.o.ls手动下载"+ e.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                System.Environment.Exit(0);
            }


            string mzsm = "本工具所提供的功能,只供学习参考之用。\n" +
            "本工具及其雇员一概毋须以任何方式就任何功能传递或传送的失误、不准确或错误对用户或任何其他人士负任何直接或间接的责任。\n" +
            "在法律允许的范围内,本工具在此声明,不承担用户或任何人士就使用或未能使用本工具所提供的功能或任何链接或项目所引致的任何直接、间接、附带、从属、特殊、惩罚性或惩戒性的损害赔偿(包括但不限于收益、预期利润的损失或失去的业务、未实现预期的节省)。\n" +
            "本工具所提供的功能,若在任何司法管辖地区供任何人士使用或分发给任何人士时会违反该司法管辖地区的法律或条例的规定或会导致本工具或其第三方代理人受限于该司法管辖地区内的任何监管规定时,则该等功能不宜在该司法管辖地区供该等任何人士使用或分发给该等任何人士。用户须自行保证不会受限于任何限制或禁止用户使用或分发本工具所提供功能的当地的规定。\n" +
            "本工具图片,文字之类版权申明,因为工具可以由注册用户自行上传图片或文字,本工具无法鉴别所上传图片或文字的知识版权,如果侵犯,请及时通知我们,本工具将在第一时间及时删除。\n" + "凡以任何方式使用本工具或直接、间接使用本工具资料者,视为自愿接受本工具声明的约束。";

            DialogResult dr = MessageBox.Show(mzsm, "工具免责声明", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
            if (dr != DialogResult.OK)
            {
                System.Environment.Exit(0);
            }




            InitializeComponent();
            var materialSkinManager = MaterialSkinManager.Instance;
            materialSkinManager.AddFormToManage(this);
            materialSkinManager.Theme = MaterialSkinManager.Themes.LIGHT;
            materialSkinManager.ColorScheme = new ColorScheme(Primary.BlueGrey800, Primary.BlueGrey900, Primary.BlueGrey500, Accent.LightBlue200, TextShade.WHITE);
            mainFrm = this;
            this.Text = "抖音PC直播OBS推流小助手v" + appVersion + "    问题交流群:686431951";

            try
            {
                pictureBox1.Image = Image.FromStream(WebRequest.Create("http://tools.arma3bbs.com/app/img.png").GetResponse().GetResponseStream());

            }
            catch(Exception e)
            {

            }
        }


        /// <summary>
        /// 设置过滤器开始捕获包
        /// </summary>
        /// <param name="id"></param>
        private void runDirve(string id) {

            int i = 0;
            try
            {
                i = Int32.Parse(id);
            }
            catch (Exception e1)
            {
                MessageBox.Show(e1.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

                Application.Exit();
            }
            if (devices.Count < 1 || i == -1)
            {
                MessageBox.Show("没有选择一项", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                materialRaisedButton1.Enabled = true;
                materialRaisedButton1.Text = "获取串流服务器与密钥";
                return;
            }
            // 得到指定连接设备
            device = devices[i];
            // 定义“包到达”事件
            device.OnPacketArrival += new SharpPcap.PacketArrivalEventHandler(device_OnPacketArrival);

            // 打开连接
            int readTimeoutMilliseconds = 1000;
            device.Open(DeviceMode.Promiscuous, readTimeoutMilliseconds);

            // 设置过滤器仅获取tcp包
            string filter = "tcp";
            device.Filter = filter;
            // 开始阻塞捕获包
            device.Capture();
        }




        /// <summary>
        /// 开始捕获
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void device_OnPacketArrival(object sender, CaptureEventArgs e)
        {
            //获取以太网(Ethernet)的帧
            var ent = PacketDotNet.EthernetPacket.ParsePacket(LinkLayers.Ethernet, e.Packet.Data);
            //获取ip包
            var ip = ent.PayloadPacket;
            //获取tcp包
            var tcp = ip.PayloadPacket;
            //格式化tcp包,可直接读取tcp包中的相应值
            var tcp_packet = new TcpPacket(new ByteArraySegment(tcp.Bytes));
            if (tcp != null)
            {
                DateTime time = e.Packet.Timeval.Date;
                if (tcp.PayloadData != null)
                {
                    string data = BitConverter.ToString(tcp.PayloadData);
                    if (comboBoxType == 0) {
                        GetPcPush(data);
                    } else if (comboBoxType == 1) {
                        GetMobileGamePush(data);
                    } else if (comboBoxType == 2) {
                        GetMobileVideoPush(data);
                    }
                }

            }
        }

        /// <summary>
        /// 定义数据特征
        /// </summary>
        public static string rtmpHex = "72-74-6D-70-3A-2F-2F-70-75-73-68-2D-72-74-6D-70-2D-";
        public static string urlEndHex = "-00-00-09";
        public static string singHex = "26-73-69-67-6E-3D-";
        public static string streamHex = "73-74-72-65-61-6D-";
        public static string releaseStreamHex = "72-65-6C-65-61-73-65-53-74-72-65-61-6D-";
        public static string Pri = "";
        public static string Push = "";
        /// <summary>
        /// 获取手机游戏推流码
        /// </summary>
        /// <param name="data"></param>
        public static void GetMobileGamePush(string data) {
            string str = data;
            string p = "26-70-72-69-3D-";
            if (str.Contains(p) && Pri == "") {
                Pri = str.Substring(3, str.Length-3);
            }

            if (str.Contains(releaseStreamHex) && Push == "")
            {
                //推流密钥
                string[] strArr = str.Replace(releaseStreamHex, "").Split(new[] { streamHex }, StringSplitOptions.None);
                Push =streamHex + strArr[1];
            }
            if (Pri != "" && Push != "")
            {
                string l = "";
                if (Push.Contains(p))
                {
                    l = Push;
                }
                else {
                    Console.WriteLine("PUSH:" + hexToString(Push));
                    Console.WriteLine("PRI:" + hexToString(Pri));
                    l = Push +"-"+ Pri;
                }
                Form1.mainFrm.materialSingleLineTextField2.BeginInvoke(new InvokeDelegate(InvokeMethod), new object[] { hexToString(l), "" });
            }
            if (str.Contains(rtmpHex))
            {
                //推流服务器地址
                string[] strArr = str.Split(new[] { rtmpHex }, StringSplitOptions.None);
                string url = strArr[1].Replace(urlEndHex, "");
                Form1.mainFrm.materialSingleLineTextField2.BeginInvoke(new InvokeDelegate(InvokeMethod), new object[] { "", hexToString(rtmpHex + url) });
            }
        }

        public static string UrlMobileVideoData = "";
        public static string PushMobileVideoData = "";
        /// <summary>
        /// 获取移动视频推流码
        /// </summary>
        /// <param name="data"></param>
        public static void GetMobileVideoPush(string data) {
            string str = data;
            if (str.Contains(releaseStreamHex) && str.Contains(singHex) && PushMobileVideoData == "")
            {
                //推流密钥
                string[] strArr = str.Replace(releaseStreamHex, "").Split(new[] { streamHex }, StringSplitOptions.None);
                PushMobileVideoData = strArr[1];
                Form1.mainFrm.materialSingleLineTextField2.BeginInvoke(new InvokeDelegate(InvokeMethod), new object[] { hexToString(streamHex + PushMobileVideoData), "" });
            }

            if (str.Contains(rtmpHex) && UrlMobileVideoData == "")
            {
                //推流服务器地址
                string[] strArr = str.Split(new[] { rtmpHex }, StringSplitOptions.None);
                UrlMobileVideoData = strArr[1].Replace(urlEndHex, "");
                Form1.mainFrm.materialSingleLineTextField2.BeginInvoke(new InvokeDelegate(InvokeMethod), new object[] { "", hexToString(rtmpHex+ UrlMobileVideoData) });
            }
        }


        public static string UrlPcData = "";
        public static string PushPcData = "";
        /// <summary>
        /// 获取电脑直播推流码
        /// </summary>
        /// <param name="data"></param>
        public static void GetPcPush(String data)
        {
            //根据需要,获取tcp的data数据
            string str = data;
            string urlHex = "74-63-55-72-6C-";
            
            if (str.Contains(releaseStreamHex) && str.Contains(singHex) && UrlPcData =="")
            {
                //推流密钥
                string[] strArr = str.Replace(releaseStreamHex, "").Split(new[] { streamHex }, StringSplitOptions.None);
                UrlPcData = strArr[1];
                Form1.mainFrm.materialSingleLineTextField2.BeginInvoke(new InvokeDelegate(InvokeMethod), new object[] { hexToString(streamHex + UrlPcData), "" });
            }
            if (str.Contains(rtmpHex) && PushPcData == "")
            {
                //推流服务器地址
                string[] strArr = str.Split(new[] { urlHex }, StringSplitOptions.None);
                PushPcData = strArr[1].Substring(9).Replace(urlEndHex, "");
                Form1.mainFrm.materialSingleLineTextField2.BeginInvoke(new InvokeDelegate(InvokeMethod), new object[] { "", hexToString(PushPcData) });
            }
        }

        /// <summary>
        /// 线程委托
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        public static void InvokeMethod(string d1, string d2)
        {
            if (d1 != "")
            {
                Form1.mainFrm.materialSingleLineTextField2.Text = d1;
                //MessageBox.Show("推流密钥获取成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
            }

            if (d2 != "")
            {
                Form1.mainFrm.materialSingleLineTextField1.Text = d2;
                //MessageBox.Show("推流服务器地址获取成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
            }

            if (Form1.mainFrm.materialSingleLineTextField2.Text.Length>= 1 && Form1.mainFrm.materialSingleLineTextField1.Text.Length>= 1)
            {
                if (null != device)
                {
                    device.StopCapture();
                }
            }
           
            Form1.mainFrm.materialRaisedButton1.Enabled = false;
            Form1.mainFrm.materialRaisedButton1.Text = "成功";
           

        }







        /// <summary>
        /// 数据转换
        /// </summary>
        /// <param name="hexValues"></param>
        /// <returns></returns>
        private static string hexToString(string hexValues)
        {
            string[] hexValuesSplit = hexValues.Split('-');
            StringBuilder sb = new StringBuilder();
            foreach (String hex in hexValuesSplit)
            {
                int value = Convert.ToInt32(hex, 16);
                string stringValue = Char.ConvertFromUtf32(value);
                sb.Append(stringValue);
            }
            return sb.ToString();
        }


        
        /// <summary>
        /// 界面载入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {



            List<string> cb2 = new List<string>();
            cb2.Add("1000粉 电脑游戏直播");
            cb2.Add("50粉 手机手游直播");
            cb2.Add("0粉 手机视频直播");
            comboBox2.DataSource = cb2;
            // 无连接
            if (devices.Count < 1)
            {
                MessageBox.Show("没有找到网卡驱动!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                Application.Exit();
            }


            List<string> temp2 = new List<string>();
            string temp = "";
            Regex r = new Regex("FriendlyName: .*\n"); //匹配连接的FriendlyName
            Match m;
            // 打印连接设备
            foreach (ICaptureDevice dev in devices)
            {
                temp = dev.ToString();
                m = r.Match(temp);
                temp2.Add(m.ToString().Replace("FriendlyName:",""));
            }
            comboBox1.DataSource = temp2;


        }

 


        private void comboBox2_SelectedIndexChanged(object sender,System.EventArgs e)

        {
            comboBoxType = comboBox2.SelectedIndex;
        }




        /// <summary>
        /// 开始获取
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void materialRaisedButton1_Click(object sender, EventArgs e)
        {
            comboBox1.Enabled = false;
            comboBox2.Enabled = false;
            materialRaisedButton1.Enabled = false;
            materialRaisedButton1.Text = "等待开播获取...";
            //线程委托
            Action<string> rd = runDirve;
            rd.BeginInvoke(comboBox1.SelectedIndex.ToString(), null, null);
        }

        private void materialFlatButton1_Click(object sender, EventArgs e)
        {
            if(null != device)
            {
                device.StopCapture();
            }
           
            Application.Exit();
        }

 

        private void materialFlatButton2_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("https://it.o.ls");
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(materialSingleLineTextField1.Text);
            
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(materialSingleLineTextField2.Text);
            
        }

        private void label2_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("https://www.bilibili.com/video/BV1rq4y1n7jd?p=2");
        }
    }
}

里面用到了皮肤代码可以自己删除!!!

经常遇到的问题:

  • 为什么OBS会一直重连?答:操作有问题,仔细看上面的视频,获取到了推流密钥等直播伴侣不能关播,打开OBS输入推流地址和密钥后点击OBS中的开始推流,然后直接关闭直播伴侣,不能点击关闭直播!
  • 为什么软件打不开没反应?答:安装压缩包内的一个软件
  • 正确下播步骤:OBS中点击停止推流,然后再次打开直播伴侣会弹出重连,点击确定,然后点击关闭直播。