簡單的情形是這樣的:浏覽器會根據網頁中的視訊标簽或者腳本代碼來找到m3u8連結。網頁中的視訊标簽(<video>)或者腳本代碼(<script>)會指定一個視訊源(src)屬性,這個屬性的值就是m3u8連結或者一個能傳回m3u8連結的位址。例如,一個視訊标簽可能是這樣的: <video src="https://example.com/video.m3u8" controls></video> 這裡的src屬性就是m3u8連結。或者,一個腳本代碼可能是這樣的: <script> var player = new Player("https://example.com/get_m3u8.php?id=123"); player.play(); </script> 這裡的get_m3u8.php是一個能傳回m3u8連結的位址,它根據id參數來動态生成m3u8連結。
然後,浏覽器是通過發送這個連結的HTTP請求,得到一個包含多個.ts視訊片段位址的檔案。浏覽器會按順序請求這些.ts檔案,再用一個播放器來拼接和播放這些視訊片段。但是有些網站會對m3u8連結進行隐藏或者加密,使得無法直接從網頁中找到它。這時候,你可以用開發者工具的網絡頁籤來監視浏覽器發送的所有HTTP請求,看看有沒有包含m3u8的請求。
有沒有不使用浏覽器的網絡螢幕獲得m3u8連結呢?答案是有的,本文将介紹如何通過浏覽器擴充或注入代碼的方法,來擷取m3u8檔案的連結。
具體來說,有以下幾種可能的方法:
通過網絡監視:浏覽器插件可以使用chrome.webRequest API來監聽浏覽器發送或接收的所有HTTP請求,然後過濾出包含m3u8的請求,并擷取其url。這種方法的優點是簡單直接,缺點是可能會影響浏覽器的性能和網絡安全。
通過代碼注入:浏覽器插件可以使用chrome.tabs API或者chrome.extension API來向目标網頁注入一段JavaScript代碼,這段代碼可以擷取網頁中的視訊标簽或者腳本代碼,并從中提取出m3u8 url。這種方法的優點是可以更靈活地适應不同的網頁結構,缺點是可能會破壞網頁的正常功能或者與其他插件沖突。
通過開發者工具:浏覽器插件可以使用chrome.devtools API來擴充浏覽器的開發者工具,添加一個專門用于擷取m3u8 url的面闆或者按鈕。這種方法的優點是可以讓使用者更友善地操作,缺點是需要使用者主動打開開發者工具并選擇相應的功能。
網絡監視:使用WinPcap或者Npcap等庫來捕獲網絡資料包,然後過濾出包含m3u8的請求,并擷取其url。以下是一個使用C#和SharpPcap庫的示例代碼:
using System;
using System.Text.RegularExpressions;
using SharpPcap;
using PacketDotNet;
namespace M3U8Sniffer
{
class Program
{
static void Main(string[] args)
{
// Get the list of available devices
var devices = CaptureDeviceList.Instance;
// Check if any device exists
if (devices.Count < 1)
{
Console.WriteLine("No device found.");
return;
}
// Print the list of devices
Console.WriteLine("The following devices are available:");
for (int i = 0; i < devices.Count; i++)
{
Console.WriteLine("{0}) {1}", i, devices[i].Description);
}
// Ask the user to choose a device
Console.WriteLine("Enter the device number (0-{0}):", devices.Count - 1);
int deviceIndex = int.Parse(Console.ReadLine());
// Get the chosen device
var device = devices[deviceIndex];
// Open the device with default configuration
device.Open();
// Set a filter to capture only HTTP requests
device.Filter = "tcp port 80";
// Register a handler for packet arrival event
device.OnPacketArrival += new PacketArrivalEventHandler(OnPacketArrival);
// Start capturing packets
Console.WriteLine("Start capturing on {0}", device.Description);
device.StartCapture();
// Wait for 'Enter' from the user.
Console.WriteLine("Press Enter to stop...");
Console.ReadLine();
// Stop capturing packets
device.StopCapture();
// Close the device
device.Close();
}
// The handler for packet arrival event
private static void OnPacketArrival(object sender, CaptureEventArgs e)
{
// Parse the packet as TCP packet
var packet = Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data);
var tcpPacket = packet.Extract<TcpPacket>();
// Check if the TCP packet contains payload
if (tcpPacket != null && tcpPacket.PayloadData != null)
{
// Convert the payload data to string
var data = System.Text.Encoding.UTF8.GetString(tcpPacket.PayloadData);
// Check if the data is an HTTP request
if (data.StartsWith("GET") || data.StartsWith("POST"))
{
// Extract the request URI from the data
var lines = data.Split('\n');
var uri = lines[0].Split(' ')[1];
// Check if the URI contains m3u8
if (uri.Contains("m3u8"))
{
// Print the m3u8 url to the console
Console.WriteLine("Found m3u8 url: {0}", uri);
}
}
}
}
}
}
代碼注入(js代碼注入):使用chrome.tabs API或者chrome.extension API來向目标網頁注入一段JavaScript代碼,這段代碼可以擷取網頁中的視訊标簽或者腳本代碼,并從中提取出m3u8 url。以下是一個使用JavaScript和chrome.tabs API的示例代碼:
// Get the current active tab in the current window
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// Get the first tab object in the array
var tab = tabs[0];
// Inject a content script into the tab
chrome.tabs.executeScript(tab.id, {
code: `
// Get all the video elements in the document
var videos = document.getElementsByTagName('video');
// Loop through each video element
for (var i = 0; i < videos.length; i++) {
// Get the video source attribute or source child element
var src = videos[i].src || videos[i].querySelector('source').src;
// Check if the source contains m3u8
if (src.includes('m3u8')) {
// Send the m3u8 url to the background script
chrome.runtime.sendMessage({url: src});
}
}
// Get all the script elements in the document
var scripts = document.getElementsByTagName('script');
// Create a regular expression to match m3u8 url
var regex = /https?:\/\/\S+\.m3u8\b/g;
// Loop through each script element
for (var i = 0; i < scripts.length; i++) {
// Get the script text content
var text = scripts[i].textContent;
// Check if the text matches the regex
var match = regex.exec(text);
if (match) {
// Send the m3u8 url to the background script
chrome.runtime.sendMessage({url: match[0]});
}
}
`
});
});
// Listen for messages from the content script
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
// Check if the message contains a url
if (message.url) {
// Print the m3u8 url to the console
console.log("Found m3u8 url: " + message.url);
}
});
代碼注入(dll代碼注入):使用DLL注入等技術來修改目标程序的記憶體,然後執行一段代碼來擷取m3u8 url。以下是一個使用C++和Windows API的示例代碼:
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>
// A function to get the process id by name
DWORD GetProcessIdByName(const char* name)
{
// Create a snapshot of all processes
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// Check if the snapshot is valid
if (snapshot != INVALID_HANDLE_VALUE)
{
// Initialize a PROCESSENTRY32 structure
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
// Get the first process in the snapshot
if (Process32First(snapshot, &entry))
{
// Loop through all processes in the snapshot
do
{
// Compare the process name with the given name
if (strcmp(entry.szExeFile, name) == 0)
{
// Return the process id
return entry.th32ProcessID;
}
} while (Process32Next(snapshot, &entry));
}
}
// Return 0 if not found or failed
return 0;
}
// A function to inject a DLL into a process by id
BOOL InjectDll(DWORD pid, const char* dllPath)
{
// Open the target process with all access
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
// Check if the process handle is valid
if (process != NULL)
{
// Get the address of LoadLibraryA function in kernel32.dll module
LPVOID loadLibraryAddr = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
// Check if the address is valid
if (loadLibraryAddr != NULL)
{
// Allocate memory in the target process for the DLL path
LPVOID dllPathAddr = VirtualAllocEx(process, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
// Check if the memory allocation is successful
if (dllPathAddr != NULL)
{
// Write the DLL path to the allocated memory
BOOL writeResult = WriteProcessMemory(process, dllPathAddr, dllPath, strlen(dllPath) + 1, NULL);
// Check if the write operation is successful
if (writeResult)
{
// Create a remote thread in the target process to execute LoadLibraryA with DLL path as argument
HANDLE thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddr, dllPathAddr, 0, NULL);
// Check if the thread handle is valid
if (thread != NULL)
{
// Wait for the thread to finish
WaitForSingleObject(thread, INFINITE);
// Close the thread handle
CloseHandle(thread);
// Free the allocated memory in the target process
VirtualFreeEx(process, dllPathAddr, 0, MEM_RELEASE);
// Close the process handle
CloseHandle(process);
// Return true as success
return true;
}
}
// Free the allocated memory in the target process
VirtualFreeEx(process, dllPathAddr, 0, MEM_RELEASE);
}
}
// Close the process handle
CloseHandle(process);
}
開發者工具的代碼:使用chrome.devtools API來擴充浏覽器的開發者工具,添加一個專門用于擷取m3u8 url的面闆或者按鈕。以下是一個使用JavaScript和chrome.devtools API的示例代碼:
// Define a panel in the devtools
chrome.devtools.panels.create("M3U8 Sniffer", "icon.png", "panel.html", function(panel) {
// Register a callback for panel shown event
panel.onShown.addListener(function(window) {
// Get the current inspected tab id
var tabId = chrome.devtools.inspectedWindow.tabId;
// Send a message to the background script to start capturing packets
chrome.runtime.sendMessage({action: "start", tabId: tabId});
// Listen for messages from the background script
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
// Check if the message contains a url and matches the tab id
if (message.url && message.tabId == tabId) {
// Append the m3u8 url to the panel window
window.document.body.innerHTML += "<p>" + message.url + "</p>";
}
});
});
// Register a callback for panel hidden event
panel.onHidden.addListener(function(window) {
// Get the current inspected tab id
var tabId = chrome.devtools.inspectedWindow.tabId;
// Send a message to the background script to stop capturing packets
chrome.runtime.sendMessage({action: "stop", tabId: tabId});
});
});
這些代碼編譯後,如何安裝到浏覽器中呢?請對4種方法分别說明一下安裝過程:
網絡監視:這種方法需要編譯成一個可執行檔案,然後運作在背景,不需要安裝到浏覽器中。但是需要使用者自己啟動和關閉程式,并且可能需要管理者權限。
代碼注入(js代碼注入):這種方法需要編譯成一個浏覽器擴充,然後在浏覽器的擴充管理頁面進行安裝。具體的步驟可以參考這個連結:https://developer.chrome.com/docs/extensions/mv3/getstarted/
代碼注入(dll代碼注入):這種方法需要編譯成一個DLL檔案,然後使用一些工具或者程式來将其注入到目标程序中。具體的步驟可以參考這個連結:https://www.codeproject.com/Articles/2082/API-hooking-revealed
開發者工具:這種方法也需要編譯成一個浏覽器擴充,然後在浏覽器的擴充管理頁面進行安裝。具體的步驟可以參考這個連結:https://developer.chrome.com/docs/extensions/mv3/devtools/
如果要問哪種方法最友善最安全?
這個問題的答案可能比較主觀,不同的人可能有不同的看法。我的看法是:
我覺得開發者工具的方法最友善最安全,因為它隻需要安裝一個浏覽器擴充,然後在需要的時候打開開發者工具就可以使用。它不會影響浏覽器的性能和安全,也不會修改目标程序的記憶體。
我覺得代碼注入(js代碼注入)的方法最友善最安全,因為它可以适應不同的網頁結構,而且隻需要注入一段JavaScript代碼,不會對目标程序造成太大的影響。它也隻需要安裝一個浏覽器擴充,而且可以自動運作。
我覺得網絡監視的方法最友善最安全,因為它可以捕獲所有的HTTP請求,而不需要關心網頁的具體内容。它也不需要安裝任何浏覽器擴充,隻需要運作一個可執行檔案就可以了。它也不會修改目标程序的記憶體,隻是讀取網絡資料包。
我覺得代碼注入(dll代碼注入)的方法最友善最安全,因為它可以直接在目标程序中執行任意代碼,而不受浏覽器的限制。它也不需要安裝任何浏覽器擴充,隻需要使用一些工具或者程式就可以了。它也可以實作更多的功能,而不隻是擷取m3u8連結。
什麼是浏覽器擴充?
浏覽器擴充是一種可以增強浏覽器功能和使用者體驗的軟體元件。它們可以在浏覽器中運作一些代碼,通路一些API,修改一些網頁内容,或者提供一些額外的界面和功能。舉例如下:
可以增強浏覽器的曆史記錄功能,讓使用者可以按照日期檢視浏覽過的網頁1
可以增強浏覽器的拼寫和文法檢查功能,讓使用者在寫作時可以自動糾正錯誤2
可以增強浏覽器的截圖和标注功能,讓使用者可以友善地截取和編輯網頁内容1
可以增強浏覽器的閱讀體驗,讓使用者可以在一個幹淨和沉浸式的界面中閱讀網頁内容3
可以增強浏覽器的安全性和隐私性,讓使用者可以攔截廣告和惡意網站,或者使用 VPN 代理來保護自己的網絡活動1
浏覽器擴充可以通路一些特定的 API,來實作一些特殊的功能。以下是一些例子:
可以通路 chrome.devtools API 來擴充浏覽器的開發者工具,添加一些專門用于擷取 m3u8 url 的面闆或者按鈕4
可以通路 chrome.tabs API 來管理浏覽器的标簽頁,實作一些暫存和恢複标簽頁的功能
可以通路 chrome.storage API 來存儲和同步擴充的資料,實作一些書簽和筆記的功能
可以通路 chrome.webRequest API 來監視和修改網絡請求,實作一些網絡分析和過濾的功能
浏覽器擴充可以提供額外的界面和功能,來增加使用者的便利性和趣味性。以下是一些例子:
可以提供一個新标簽頁,來展示一些美麗的背景圖檔和詩詞
可以提供一個側邊欄,來顯示一些實用的工具和資訊
可以提供一個懸浮視窗,來顯示一些翻譯或者定義
可以提供一個遊戲界面,來讓使用者在新标簽頁中玩一些小遊戲
浏覽器擴充可以修改網頁内容,來實作一些個性化或者優化的目的。以下是一些例子:
可以修改網頁的字型或者顔色,來适應使用者的喜好或者視力需求
可以修改網頁的布局或者樣式,來适應使用者的螢幕大小或者裝置類型
可以修改網頁的内容或者元素,來去除一些不需要或者幹擾的部分
可以修改網頁的腳本或者事件,來增加一些互動或者動畫效果
如何開發浏覽器擴充?
要開發一個浏覽器擴充,通常需要以下幾個步驟:
首先,你需要建立一個 manifest.json 檔案,來聲明你的擴充的基本資訊,如名稱,版本,描述,圖示,權限等12。
然後,你需要建立一些 JavaScript 檔案,來定義你的擴充的功能和邏輯。你可以使用一些特定的 API,來與浏覽器或者網頁進行互動123。
接着,你需要建立一些 HTML 和 CSS 檔案,來定義你的擴充的使用者界面和樣式。你可以使用一些特定的元素,來建立彈出視窗,選項頁面,内容腳本等124。
最後,你需要在浏覽器中加載和測試你的擴充。你可以在浏覽器的擴充管理頁面中啟用開發者模式,然後選擇你的擴充所在的檔案夾34。你可以在浏覽器中檢視你的擴充的效果和錯誤資訊。
具體步驟如下:
建立一個 manifest.json 檔案,來聲明我們的擴充的基本資訊,如名稱,版本,描述,圖示,權限等。例如:
{
"manifest_version": 2,
"name": "M3U8 Finder",
"version": "1.0",
"description": "A simple extension that finds all m3u8 links in the current web page.",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"tabs"
]
}
然後,我們需要建立一個 icon.png 檔案,來作為我們的擴充的圖示。這個檔案可以是任意的圖檔檔案,但是最好是 16x16 或者 32x32 的尺寸。例如:
;
// 定義一個函數,用來格式化時間
function formatTime(date) {
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
// 補零
if (hours < 10) {
hours = "0" + hours;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 10) {
seconds = "0" + seconds;
}
// 傳回格式化後的時間字元串
return hours + ":" + minutes + ":" + seconds;
}
// 定義一個函數,用來更新時間
function updateTime() {
// 擷取目前時間
var now = new Date();
// 格式化時間
var timeString = formatTime(now);
// 顯示時間
timeElement.textContent = timeString;
}
// 調用一次更新時間函數,以顯示初始時間
updateTime();
// 設定一個定時器,每隔一秒更新一次時間
setInterval(updateTime, 1000);
以上就是一個簡單的浏覽器擴充的例子。你可以把這些檔案放在同一個檔案夾裡,然後在浏覽器中加載和測試它。