- นี่คือ PoC ที่วาง พร็อกซี MITM บน pfSense ไว้ระหว่าง Apple TV กับอินเทอร์เน็ต ถอดรหัส HTTPS แล้วแก้ไขการตอบกลับ Protobuf ที่ YouTube ส่งมา เพื่อไม่ให้มีการลงทะเบียนช่องโฆษณาบนอุปกรณ์ Apple
- ตัว ตัวบล็อก DNS และการทำ VPN routing แบบเดิมติดข้อจำกัด เช่น DNS TTL, IP ไม่ตรงกัน, ข้อผิดพลาด 403 และ ASN leak เพราะโฆษณา YouTube กับวิดีโอหลักใช้โดเมนและโครงสร้างพื้นฐานเดียวกัน
- การทดลองกับ Squid ถูกยุติลงเพราะปัญหาด้านประสิทธิภาพและการตั้งค่า จากนั้นจึงเปลี่ยนไปใช้ mitmproxy/mitmdump บน FreeBSD jail เพื่อถอดรหัสทราฟฟิก TLS และแก้ไขการตอบกลับ JSON·Protobuf โดยตรง
- YouTube บนเว็บสามารถลบ
adPlacements,playerAdParams, URLpageadเป็นต้น ออกจาก JSON ได้ แต่แอป YouTube บน iOS เก็บช่องโฆษณาและข้อมูลติดตามไว้ใน response แบบapplication/x-protobufจึงต้องจัดการกับ โครงสร้าง Protobuf - วิธีสุดท้ายคือ การสแกนเชิงเส้นและแก้ไข 1 ไบต์ โดยไล่ย้อนหา field tag ใกล้สตริง
/pagead/แล้วเปลี่ยนแท็กอย่าง50195462เป็นtarget_field_tag - 1ซึ่งให้ประสิทธิภาพเหมาะกับการประมวลผลแบบเรียลไทม์โดยไม่ต้องถอดรหัสทั้งหมด
เป้าหมายและการออกแบบเครือข่ายระยะแรก
- เป้าหมายคือสร้างเราเตอร์บน FreeBSD และ pfSense เพื่อบล็อกโฆษณา YouTube แบบ pre-roll, mid-roll และ end-roll ของ Apple TV และ iPhone ทั้งเครือข่าย
- หากวาง พร็อกซีแบบ man-in-the-middle ไว้ระหว่าง Apple TV กับอินเทอร์เน็ตภายนอก ก็จะสามารถถอดรหัสทราฟฟิก HTTPS และอ่านข้อมูล Protocol Buffer ที่ Google ใช้เติมโฆษณา YouTube ได้
- หลังจากทำระบบบล็อกโฆษณา YouTube อยู่หลายเดือน ผู้เขียนก็เริ่มจ่าย YouTube Premium และบอกว่า “ทำได้” กับ “ควรทำ” เป็นคนละเรื่องกัน
- เหตุผลที่บล็อกโฆษณาและตัวติดตามคือการติดตามข้อมูลส่วนตัว การเปลืองแบนด์วิดท์ clickbait และ cryptojacking
- มองว่าทราฟฟิกเครือข่าย 25~40% อาจเป็นโฆษณา สคริปต์ติดตาม ตัวโหลดวิเคราะห์แบบเรียลไทม์อย่าง fingerprint.js, googletagmanager.js และ Hotjar
- อธิบายว่า JavaScript ขุดคริปโตอย่าง CoinHive.js อาจทำให้คอมพิวเตอร์ร้อนเกินไปหรือถูกนำไปใช้หารายได้เล็กน้อยโดยมิชอบ
ฮาร์ดแวร์ pfSense และการตั้งค่าพื้นฐาน
- มองว่าหากต้องการปกป้องเครือข่าย SMB ทั้งหมด VM, Docker image และ Raspberry Pi มีประสิทธิภาพไม่พอ จึงต้องใช้ ฮาร์ดแวร์เฉพาะทาง ที่รับหน้าที่แค่ routing, ถอดรหัส และ monitoring แพ็กเก็ต
- ฮาร์ดแวร์เราเตอร์ที่ใช้คือ mini PC ที่มีชุดคำสั่ง AES-NI, RAM DDR4, SSD แบบ mSATA และ USB drive สำหรับแฟลช pfSense
- ตัวอย่างสเปกคือ mini PC J4125, RAM DDR4 32GiB และ SSD mSATA 128GiB
- มองว่าพื้นที่เก็บข้อมูล 128GB เพียงพอสำหรับ log, ลด SSD wear, packet capture และ edge cache ของ NPM·Docker
- อิมเมจติดตั้ง pfSense มีขนาดราว 360MB และสามารถแฟลชลง USB drive ได้ด้วย Etcher AppImage
- หลังตั้งค่าครั้งแรก AES-NI แสดงเป็น “Yes (inactive)” จึงเปิดใช้งานเองที่
System › Advanced › Miscellaneous - เพื่อใช้ประโยชน์จาก RAM 32GiB จึงจัดสรร RAM disk ให้
/varและ/tmpอย่างเพียงพอ และตั้งให้ SSD 128GiB สำรองข้อมูล RAM-disk ทุกชั่วโมงโดยคาดหวังการทำ wear-leveling - ใน Dashboard มีการเพิ่ม วิดเจ็ต S.M.A.R.T. เพื่อให้ตรวจจับความผิดปกติของ SSD ได้
การบล็อก DNS, การแยกเครือข่าย และ pfBlockerNG
- เดิมใช้ Pi-hole บน Raspberry Pi เป็นตัวบล็อกโฆษณาระดับ DNS และบน pfSense ได้ติดตั้ง pfBlockerNG-devel เพื่อทดลองบล็อกโฆษณา·เนื้อหาอันตรายและ geo-blocking
- หากบริการ pfb_dnsbl ไม่เริ่มทำงานหรือแท็บ status แสดง
[ Missing CRON task ]ก็แนะนำให้ลองลบไฟล์ว่าง/var/run/booting - ใช้พอร์ต Gigabit 3 พอร์ตของ mini PC สร้างเครือข่ายจริงแทน VLAN และแยกอุปกรณ์ที่ “โทรกลับบ้าน” อย่าง Alexa และ Apple TV ออกจากเครือข่ายหลัก
- อุปกรณ์ untrusted ถูกวางไว้ในเครือข่าย private
172.31.1.0/24 - trusted LAN ยังคงใช้
192.168/16 - ฮาร์ดแวร์ LAN สำหรับ IoT จะผ่าน adblocker และพยายามดักจับคำขอ DNS แบบ hard-coded เช่น
1.1.1.1·9.9.9.9เพื่อไม่ให้ YouTube เลี่ยงตัวบล็อก DNS ได้
- อุปกรณ์ untrusted ถูกวางไว้ในเครือข่าย private
- มีการตั้งกฎ NAT ให้ไคลเอนต์ทั้งหมดหลัง pfSense ใช้ Unbound DNS ภายในเครื่อง
- มองว่าต้องบล็อก DNS over TLS ก่อน จึงจะดักจับ DNS query ได้
- iPhone อาจแสดง Privacy Warning เกี่ยวกับการบล็อกทราฟฟิก DNS ที่เข้ารหัส แต่คำขอ DNS ไปยัง upstream จะยังเข้ารหัสไปที่ Cloudflare
- ควรปิด NAT reflection เพื่อไม่ให้อินเทอร์เน็ตภายนอกเข้าถึงเซิร์ฟเวอร์ DNS ได้
- สร้าง firewall alias ชื่อ
Non_WANเพื่อ redirect พอร์ต 53 ของ local DNS query จากทุกอินเทอร์เฟซที่ไม่ใช่ WAN ไปยัง localhost - เนื่องจาก YouTube ส่งทั้งโฆษณาและวิดีโอหลักจากโดเมนเดียวกัน จึงกรองเฉพาะโฆษณาด้วย ตัวบล็อกชื่อโดเมน อย่าง pfBlockerNG หรือ Pi-hole ได้ยาก
การทดลองเลี่ยงผ่าน VPN และจุดที่ล้มเหลว
- นอกจากการบล็อกโฆษณา ยังมีการทดลองหลอกอัลกอริทึมโฆษณา YouTube ให้มองว่าผู้ใช้มีความน่าสนใจต่อผู้ลงโฆษณาน้อยลง
- ตั้งใจใช้เราเตอร์ pfSense route ทราฟฟิกติดตามตำแหน่งของ YouTube ผ่าน VPN ไปยังภูมิภาคที่มีผู้ชมน้อย
- ตั้งเป้าให้บัญชี YouTube ถูกมองว่าเป็น “ผู้ชายอายุ 70 ปี อาศัยอยู่ในอิตาลี”
- บน pfSense ใช้ WireGuard แทน OpenVPN เพื่อทำการทดลองพื้นฐานในการส่งทราฟฟิกทั้งหมดของ Apple TV ผ่าน VPN
- ติดตั้งแพ็กเกจ WireGuard ของ FreeBSD แล้วเพิ่มและเปิดใช้งาน tunnel
- สำหรับการตั้งค่า NordLynx ได้ตรวจสอบ private key บน Linux VM ด้วย
sudo wg showconf nordlynxแล้วนำไปใส่ใน pfSense
- ผลทดสอบพบว่า Google บนโน้ตบุ๊กแสดงเป็นภาษาอิตาลี และ YouTube บน Apple TV ก็เปลี่ยนเป็นภาษาอิตาลีเช่นกัน
- โฆษณายังมีอยู่บ้าง แต่บอกว่าลดลงจากเดิม
- Netflix และ Amazon Prime มีปัญหา และดูเหมือนว่าไฟล์ CSS หรือฟอนต์ถูกบล็อก หรือ thumbnail โหลดไม่ขึ้น
- มีคำเตือนว่าอย่าส่งทราฟฟิกทั้งหมดของ Apple TV ผ่าน VPN เพราะ Netflix และ Prime ตรวจจับ VPN provider และ geofencing ได้เก่ง
- หลังจากนั้นจึงตั้ง firewall policy rule เพื่อให้ เฉพาะทราฟฟิก YouTube ของ Apple TV วิ่งผ่าน VPN โดยกำหนดเป้าหมายเช่น
www.youtube.com,youtube.com,googlevideo.com,accounts.google.com,googleapis.com,gstatic.com- ผลคือ YouTube มองว่าผู้ใช้อยู่ที่ Milan ขณะที่ Netflix และ Prime Video มองว่าอยู่ที่ Canada
- โฆษณาลดลงจนอยู่ในระดับที่ “น้อยมากและห่างกันมาก”
- หนึ่งวันต่อมาเกิด DNS race condition
- hostname alias ของ pfSense จะ resolve ทุก 300 วินาทีตามค่าเริ่มต้น
- DNS TTL ของ YouTube อาจเป็น 1,440 วินาที หรือ 24 นาที
- หาก IP ที่ Alias Daemon resolve ได้ไม่ตรงกับ IP ที่ไคลเอนต์จริงได้รับ policy ก็อาจไม่สามารถ tunnel ทราฟฟิก YouTube ได้
- วิดีโอ YouTube บางรายการเล่นไม่ได้และขึ้น
403 Forbidden- มีคำอธิบายว่า YouTube ฝัง IP ของผู้ใช้ไว้ในแต่ละคำขอ
googlevideo.com - หากโดเมนที่แปลงรูปอย่าง
r5---sn-hpa7kn76.googlevideo.comไม่ถูก tunnel คำขอก็จะออกจาก IP ที่ผิดและทำให้เกิดปัญหา - สิ่งที่ต้องการคือการ tunnel แบบ wildcard สำหรับ
*.googlevideo.comแต่ NAT และ firewall rule ทำงานกับ IP ไม่ใช่ wildcard hostname
- มีคำอธิบายว่า YouTube ฝัง IP ของผู้ใช้ไว้ในแต่ละคำขอ
PoC ติดตาม IP โดยอิงจาก DNS query
- ได้วางแนวทาง Google Video DNS query hijack เพื่อทำการ route
*.googlevideo.comผ่าน VPN- เป็นวิธีที่คอยติดตาม DNS query log เป็นระยะ แล้วเพิ่ม query ของ
*.googlevideo.comเข้าไปในรายการ alias - มองว่าหากวิดีโอแต่ละรายการใช้โดเมนที่ไม่ซ้ำและถูกดัดแปลง วิธีนี้จะไม่ทำงาน เว้นแต่จะรีเฟรชใหม่ทุกครั้งต่อวิดีโอ
- เป็นวิธีที่คอยติดตาม DNS query log เป็นระยะ แล้วเพิ่ม query ของ
- เป้าหมายใหม่คือใช้ Python 3 และ pfSense REST API เพื่อตรวจจับ DNS query, จับ IP, หน่วงการตอบกลับไว้ชั่วคราว, เพิ่ม IP เข้าไปในกฎ VPN tunneling แล้วค่อยปล่อย DNS reply
- ติดตั้ง pfSense REST API แล้วส่งคำขอ GET ไปที่
https://pfsense/api/v1/firewall/aliasเพื่อดู aliasVPN_domains - ได้สำรวจ Python module ของ Unbound DNS Resolver และล็อกข้อความ DNS-query ได้สำเร็จ
- เวอร์ชัน Python ตอนนั้นคือ 3.8
- ตัวอย่าง Unbound Python module อิงกับ Python 2.4 จึงมองว่าอาจต้องใช้
2to3หรือจัดฟอร์แมตเพิ่มเติม
- สคริปต์ PoC จะดึง IP ของ A/AAAA record จาก DNS response แล้วเพิ่มเข้าไปใน pfSense alias
- A record ประมวลผลด้วย
ipaddress.IPv4Address(d.rr_data[j][2:]).exploded - AAAA record ประมวลผลด้วย
ipaddress.IPv6Address(d.rr_data[j][2:]).exploded - ตั้งค่า alias TTL เป็น 1 ชั่วโมง และ capacity เป็น 500
- A record ประมวลผลด้วย
- วันถัดมา Unbound DNS Resolver เกิด segfault และทุกครั้งที่เพิ่ม IP ก็ต้อง reload กฎ pfSense ทำให้ pfSense ช้ามาก
เปลี่ยนจาก Squid ไปเป็น mitmproxy
- เป้าหมายใหม่เปลี่ยนไปเป็นการสำรวจและติดตั้ง proxy ตระกูล Squid, สร้าง CA certificate ปลอมที่เชื่อถือได้ แล้วถอดรหัส TLS traffic
- ในการทดลองกับ Squid ได้ทดสอบว่า
squid3proxy ที่มากับแพ็กเกจ pfSense ตอบโจทย์หรือไม่- สร้างโฟลเดอร์เฉพาะ
/squid_cacheและตั้งขนาด cache เป็น 8GiB - คาดหวังการรองรับ Transparent HTTPS
- สร้างโฟลเดอร์เฉพาะ
- หลังตั้งค่า Squid และ SquidGuard อยู่หนึ่งวันก็ล้มเลิก
- ความเร็วช้ามาก
- การตั้งค่า ACL ยุ่งยาก
- มีปัญหาที่เกี่ยวกับ
https://http/* - การอัปเดตรายการกรอง URL ของ SquidGuard ใช้เวลานานมาก
- UI ของ Squid ยังไม่เพียงพอ
- หลังจากนั้นจึงตัดสินใจใช้ mitmproxy ที่เขียนด้วย Python
- เลือก mitmproxy แทน
SSLSplitเพราะความยืดหยุ่นของ Python hook และ UI - เวอร์ชัน FreeBSD ของ pfSense คือ
12.2-Stable, 64-bit build
- เลือก mitmproxy แทน
- ในสภาพแวดล้อมปกติของ pfSense นั้น jail ถูกปิดไว้ จึงติดตั้ง
ezjailแบบแมนนวลและสร้าง jail สำหรับmitmproxy- สร้าง jail ด้วย
ezjail-admin create mitmproxy 'lo0|127.0.1.1' - ตั้งค่า
allow.raw_sockets=1สำหรับ transparent proxy mode - ระบุว่าหาก raw socket ถูกบล็อก อาจเกิดข้อผิดพลาดอย่าง
Transparent mode failureหรือCannot open connection, no hostname given.
- สร้าง jail ด้วย
- การรัน Linux tarball binary ล้มเหลวบน FreeBSD
- เกิดข้อผิดพลาด
ELF interpreter /lib64/ld-linux-x86-64.so.2 not found - และยังหา
libdl.so.2,libz.so.1,libpthread.so.0,libc.so.6ไม่พบด้วย
- เกิดข้อผิดพลาด
- ภายใน jail ได้รัน
pkg install mitmproxyโดยการติดตั้งต้องใช้ 50 แพ็กเกจ, พื้นที่เพิ่ม 206MiB และดาวน์โหลด 33MiB - เพื่อให้เข้าถึง MITMProxy ได้จาก LAN จึงผูก virtual IP
127.0.1.1เข้ากับ localhost และตั้ง NAT rule ให้ forward[Private IPs]:8080ไปที่127.0.1.1:8080ชั่วคราว - ไฟล์ CA PEM ที่ MITMProxy สร้างอัตโนมัติคือ
~/.mitmproxy/mitmproxy-ca-cert.pemและได้นำ CA cert นี้ไปติดตั้งใน Trusted Root Store ของอุปกรณ์ทดสอบ mitmproxyใช้ CPU สูงแม้อยู่ในสถานะ idle และมองว่าการสร้าง TLS certificate แบบเรียลไทม์ต่อคำขอ รวมถึง logging ที่มากเกินไป ทำให้ช้าลงอย่างมาก- มองว่า
mitmdumpใช้ CPU น้อยกว่า เพราะตัด UI และ logging ที่มากเกินไปออก
- มองว่า
ลบโฆษณา YouTube JSON บนเว็บ
- Certificate Pinning คือเทคนิคที่ server หรือ client รู้ fingerprint ของ certificate ที่คาดไว้ล่วงหน้า ทำให้การปลอม certificate ของ MITMProxy ใช้ไม่ได้
- โฮสต์ที่มีปัญหาสามารถให้ข้าม proxy ได้ด้วยตัวเลือก
--ignore-hosts- ตัวอย่างเช่น ignore
apple.com:443,icloud.com:443
- ตัวอย่างเช่น ignore
- ระหว่างเข้า YouTube พบว่า page ads แสดงให้เห็นใน MITMProxy พร้อม header ที่ไม่ได้เข้ารหัส และได้พิจารณาความเป็นไปได้ในการบล็อกด้วย regex แบบง่าย
- เพื่อใช้สคริปต์บล็อกโฆษณา YouTube ได้เพิ่ม
--scripts "youtube.py"ให้กับmitmdump - smoke-test filter จะบล็อกคำขอโฆษณาตาม substring ของ URL
youtube.com:/pagead/,/log_event?,/stats/ads,/stats/qoe?,/ptracking?,/generate_204,el=adunit,adformat=,/activeview?google.com,google.ca:/pagead/ggpht.com:.
- คำขอที่ต้องการบล็อกดูเหมือนจะถูกบล็อกจริงทั้งใน MITMProxy และแผง DevTools Network แต่โฆษณายังคงแสดงอยู่ และบางครั้งโฆษณาก็ถูกข้ามเองหรือเล่นไม่สำเร็จ
- หลังจากนั้นพบ URL ที่เกี่ยวข้องกับโฆษณาจำนวนมากภายใน JSON payload
- มีส่วน
playerAdsและplaybackTracking - ใน
youtubeRemarketingUrlมีhttps://www.youtube.com/pagead/viewthroughconversion/... - ใน
googleRemarketingUrlมีhttps://www.google.com/pagead/1p-user-list/...
- มีส่วน
- หลังวิเคราะห์ YouTube UI และ HTTP workflow รวมถึง cookies และ service workers แล้ว ระบุว่าสามารถลบโฆษณา pre-roll, post-roll และ mid-video ได้ทั้งหมด
- ในขั้นตอนนี้ สามารถลบโฆษณาออกจาก JSON payload ของโฆษณา YouTube บนเว็บ ได้ที่เราเตอร์
ปัญหา Protobuf ของ YouTube บน iOS
- แอป YouTube บน iOS แสดงข้อมูลที่คล้ายกันมากจาก Protobuf version ของ API call เดียวกับเวอร์ชันเว็บ
- ใน Protobuf คีย์เป็นตัวเลขและอาจเปลี่ยนได้ จึงใช้วิธีหา section โฆษณาด้วย JSONPath ไม่ได้
- YouTube จะส่งรายการโฆษณาจำนวนมากล่วงหน้าที่จะได้เห็นมาใน payload และเมื่อใช้รายการนั้นหมด รายการใหญ่อีกรายการก็จะถูกส่งตามมาในไม่ช้า
- ใน Protobuf payload พบสตริงอย่าง “Telus,” “Samsung TV,” “Boxing Week,” “Buy now”
- โปรโตคอล YouTube บน iOS แตกต่างจาก web traffic
- ในเวอร์ชันเว็บ ยังพอแยกวิดีโอโฆษณากับวิดีโอที่ต้องการได้ระดับหนึ่งจาก URL และ query parameter
range - โปรโตคอลบน iOS ไม่ใช้ทั้ง query parameter
rangeและ headerRangeแต่ใช้ตัวนับอย่าง&nr=2,&nr=3กับ video chunk แทน - เพื่อบล็อกโฆษณาบน iOS จึงต้อง reverse-engineer Protobuf response
- ในเวอร์ชันเว็บ ยังพอแยกวิดีโอโฆษณากับวิดีโอที่ต้องการได้ระดับหนึ่งจาก URL และ query parameter
- ใน decoded Protobuf message พบรายการ
has_unlimited_entitlement: False,has_premium_lite_entitlement: Falseแต่แทนที่จะ toggle ค่านี้ ก็กลับไปใช้ heuristics - การ decode Protobuf ดิบขนาดราว 500KiB ด้วย Python pure implementation ช้ามาก
- บนเดสก์ท็อป i7-6700 ผลลัพธ์ของ Python อยู่ที่ประมาณ 2.06~2.11 วินาที
- บนเราเตอร์ pfSense ผลลัพธ์ของ Python อยู่ที่ประมาณ 22.8~24.2 วินาที
- C++
protoc --decode_rawใช้เวลาบนเดสก์ท็อปประมาณ 0.017~0.022 วินาที และบนเราเตอร์ pfSense ประมาณ 0.12~0.14 วินาที
ความพยายามถอดรหัส Protobuf และดึง schema
- เนื่องจาก Python ไม่รองรับ raw Protobuf decoding จึงเลือกวิธีสื่อสารกับไบนารี C++
protocผ่านsubprocess.Popenแทนการเรียกใช้ C++libprotobuf.soโดยตรง - ลอง fuzzing การตอบกลับของวิดีโอโฆษณาด้วยการทำ
200,404,503ว่าง ๆ, response body ที่ถูกตัด, การทำบางส่วนของวิดีโอโฆษณาเป็น null เป็นต้น แต่แอป iOS จะค่อย ๆ ช้าลงแล้ว crash หรือค้างอยู่ที่หน้าจอโฆษณา - การบล็อก URL กระตุ้นให้แอปมีพฤติกรรมตอบโต้ และใน video response chunk ก็มี session metadata รวมอยู่ด้วย
- blackboxprotobuf สำหรับ Burp Suite สามารถ decode raw Protobuf wire message, inject เนื้อหา แล้ว encode กลับเพื่อดูการทำงานของ Protobuf endpoint ได้
- แนะนำให้ใช้เวอร์ชันต้นฉบับของ Burp Suite ไม่ใช่ PyPI fork
- บาง fork มีปัญหา stack overflow หรือ infinite recursion เพราะ deep recursion
- หากใช้ C++ bindings จะ transcode raw Protobuf ราว 500KiB ได้ภายในไม่กี่วินาที
- schema ที่สร้างขึ้นไม่สมบูรณ์ มีขนาดใหญ่ ซ้อนลึก และ pretty-print ช้า แต่ก็เพียงพอสำหรับการหา detail ของโฆษณา
- มีการลองใช้ PBTK, Apktool, dex2jar, Java Decompiler เพื่อดึง
.protoหรือไฟล์ schema จริงจาก YouTube APK บน Android- สิ่งที่ PBTK ดึงออกมาได้มีเพียง proto file ขนาด 59 ไบต์
- แม้ใน Java จะมี Protobuf classes และ getter/setter แต่ก็ไม่ได้ schema files ที่แท้จริง จึงหยุดการลองในทางนี้
จุดเปลี่ยนสุดท้าย: เปลี่ยน 1 ไบต์ของ Protobuf field tag
- จาก decrypted network traffic และผลของ Protobuf fuzzing พบว่าโฆษณาอยู่ในโครงสร้างที่ถูกลงทะเบียนไว้กับ slots ของวิดีโอแต่ละรายการ
- ประเภทของ slot มีทั้ง pre-roll, mid-roll, end-roll, full-page, ad pods
- หากบล็อก URL โฆษณา จะเกิดข้อผิดพลาดในลักษณะ “โฆษณาที่ไม่มีอยู่จอง slot ไว้” และทำให้ UI panic
- มองว่าการ decode, edit, แล้ว re-encode โดยไม่มี schema ต้นฉบับจะได้ encoding ที่เปลี่ยนไป และไม่รู้ว่าใช้ ZigZag หรือไม่, เป็นชนิดตัวเลขแบบ
int32,int64,sint32/64,varintอะไร รวมถึงลำดับ object field ก็มัก nondeterministic จึงเป็นปัญหา - พบช่องทางอ้อมจาก backward compatibility ของ Protobuf และพฤติกรรมของ UnknownFieldSet
- เมื่อซอฟต์แวร์เก่าอ่าน message ที่มี field ใหม่เพิ่มเข้ามา อาจเกิด unknown field ได้
- หากเปลี่ยน field key บางตัวเป็นค่าอื่น อาจทำให้ sub-structure ทั้งก้อนที่มีข้อมูลโฆษณาและ tracking กลายเป็นสถานะ unavailable ได้
- ยกตัวอย่างแนวคิดการเปลี่ยน field key
49399797เป็น49399796เพื่อทำให้ ad/tracking sub-structure นั้นกลายเป็น unknown field - field key
49399797หาไม่เจอด้วยการค้นหา hex แบบตรง ๆ และต้องคำนึงถึง varint/tag encoding- wire type คือ
2หมายถึง nested string/message แบบ length-delimited - ลำดับไบต์ tag ของ target field key
49399797คือAA FF B8 BC 01 - เมื่อตัด 3 บิตของ wire type ออกด้วย
395198378 >> 3ก็จะได้ field key เดิม49399797
- wire type คือ
- ใช้ classic ad URL signature อย่าง
/pagead/ใน Protobuf bytes เพื่อกำหนดขอบเขตการค้นหา field แล้วไล่ย้อนกลับจากตำแหน่งนั้นเพื่อหาทั้ง field tag และ field key ที่ต้องแก้ไข - ในตัวอย่าง intercept log พบ key
49399797ที่ตำแหน่ง4465และ key50195462ที่ตำแหน่ง4477ในการตอบกลับapplication/x-protobufขนาด 1.87MiB ของคำขอ POST ไปยังyoutubei.googleapis.com:443/youtubei/v1/browse?key=... - ใน O(n) smoke test มีการสแกน Protobuf data ขนาด 1.8MiB เพียงหนึ่งรอบโดยไม่ใช้หน่วยความจำเพิ่ม
- พบ target ที่ไบต์ลำดับที่ 30,593 จากทั้งหมด 1.8MiB
- ย้อนกลับราว 600 ไบต์เพื่อหา field key ที่จะ denature
- เมื่อวิธีนี้ใช้งานได้ ก็ไม่จำเป็นต้องบล็อก URL ที่มี
*.googleadservices.comหรือ/pagead/อีกต่อไป และคำขอเหล่านั้นก็จะไม่ถูกส่งออกมาตั้งแต่แรก
โครงสร้างสคริปต์ add-on ของ MITMProxy
- สคริปต์ add-on ของ MITMProxy ถูกนำเสนอเป็น proof of concept สำหรับบล็อกโฆษณา YouTube บนอุปกรณ์ Apple ที่เชื่อมต่อเครือข่าย
- ชื่อไฟล์คือ
youtube.py - ตัวอย่างการรันคือ
mitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py" - prerequisite ของ FreeBSD คือ
pkg install protobuf,pkg install py38-pip,pip install jsonpath-ng
- ชื่อไฟล์คือ
- ในสคริปต์มี fairness function ที่ยอมให้โฆษณาแสดง 5% เพื่อสนับสนุนครีเอเตอร์
in_allowed_ads_window()จะข้ามการบล็อกโฆษณาหากเวลาปัจจุบันอยู่ระหว่างนาทีที่ 0 ถึง 2 ของทุกชั่วโมง
YouTubeAdBlockerจะ intercept โดเมนที่เกี่ยวข้องกับ YouTube และแก้ไข response แบบ JSON หรือ Protobuf เพื่อลบข้อมูลโฆษณา- host regex ที่เป็นเป้าหมายของการ intercept คือ
\.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com - สตริงสำหรับตรวจจับโฆษณา Protobuf คือ
b"/pagead/" - ขีดจำกัดการค้นหาคือ
80_000ไบต์ - target field tag คือ
50195462
- host regex ที่เป็นเป้าหมายของการ intercept คือ
- รายการบล็อกในขั้นตอน request มี
pagead/,log_event?,stats/ads,stats/qoe?,ptracking?,generate_204,error_204,adformat=,activeview?,_ad_,ai?,sw.jsเป็นต้น บนโฮสต์ YouTube - JSON replacement สำหรับ YouTube เวอร์ชันเว็บจะลบหรือปิดการทำงานของฟิลด์ที่เกี่ยวข้องกับโฆษณา
yt_adเปลี่ยนเป็น"0"adPlacementsเปลี่ยนเป็น[]adPlacementRenderer,adPlacementConfig,playerAdParams,gutParamsเปลี่ยนเป็น{}adVideoIdเปลี่ยนเป็น""showCompanion,showInstream,useGutเปลี่ยนเป็นFalse
- hook
load()จะปิดการใช้งาน HTTP/2 และตั้งค่าanticomp=True,mode="transparent" - hook
running()จะอัปเดตallow_hostsเพื่อให้การ intercept ใช้กับเฉพาะโดเมนที่เกี่ยวข้องกับ YouTube - hook
response()จะค้นหา/pagead/ภายใน 80,000 ไบต์แรกของ response body หากcontent-typeมีprotobuf- หากพบ จะสร้าง target tag bytes ด้วย
TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED) - จะสร้าง tag bytes ใหม่ของ
target_field_tag - 1 - จะค้นหาแบบย้อนกลับก่อนถึงตำแหน่ง
/pagead/เพื่อหา target tag - จะแทนที่ bytes ณ ตำแหน่งนั้นด้วย bytes ที่สอดคล้องกับ
target_field_tag - 1 - จากนั้นจะใส่ Protobuf content ที่แก้ไขแล้วกลับเข้าไปด้วย
flow.response.set_content(bytes(body))
- หากพบ จะสร้าง target tag bytes ด้วย
- คอมเมนต์ในโค้ดระบุว่า PoC นี้บล็อกโฆษณาได้แล้ว 90% และเสริมว่าในส่วนอื่นยังมี field key อื่น ๆ และอาจมีหลายส่วนของโฆษณาที่ต้องทำให้ใช้งานไม่ได้
ประสิทธิภาพ ข้อจำกัด และผู้ใช้เป้าหมาย
- เทคนิคสุดท้ายอาศัยความสามารถของ Protobuf ที่ยอมรับ unknown field เพื่อให้เข้ากันได้แบบ backward-compatible เมื่อ schema เปลี่ยน และอาศัย single-byte edit sensitivity ของฟอร์แมตแบบ compact
- หากเปลี่ยน 1 ไบต์ในจุดสำคัญจนทำให้ส่วนที่ซ้อนลึกดูเหมือนเป็นของ future schema version, Protobuf จะ ignore ส่วนนั้นและลบข้อมูลโฆษณาได้
- Google ส่งคืน response Protobuf ขนาดใหญ่ที่รวมถึง layout ของแอป iOS ด้วย โดย payload ตัวอย่างมีขนาด 1.8MiB
- ผู้เขียนระบุว่าการ parse payload ทั้งก้อนต้องใช้ native code อย่าง C++/Swift และการ decode ด้วย Python ช้ากว่าหลายหลัก จนทำให้เกิด connection timeout
- JSON แบบเว็บต้อง parse, edit และ re-serialize payload ทั้งหมด แต่เทคนิค Protobuf ใช้เพียง linear scan และ backtrack อย่างรวดเร็ว จึงประมวลผลได้ในระดับ microseconds เหมาะกับการบล็อกโฆษณาแบบ real-time และไม่ต้องมี blocklist
- URL ทั้งหมดของ
*.googleadservices.comและ/pagead/*บนอุปกรณ์ Apple มาจาก Protobuf payload ดังนั้นเมื่อข้อมูลโฆษณาหายไปจาก payload แล้ว request เหล่านั้นก็จะหายไปเองโดยอัตโนมัติ - แอป YouTube จะไม่พยายาม fetch ad URL ทำให้รู้สึกเร็วขึ้น และเนื้อหาจะเล่นได้ทันทีเพราะไม่มีการลงทะเบียนโฆษณาใน video slot
- วิธีนี้ถูกนำเสนอเป็นเทคนิคที่ highly specialized สำหรับบล็อกโฆษณา YouTube บนอุปกรณ์ Apple หรือทราฟฟิก tracker ของ Instagram, WhatsApp และ Facebook
- ผู้เขียนระบุว่าความต้องการ CPU สำหรับการ decrypt/re-encrypt ทราฟฟิก HTTPS สูงเกินความสามารถของ Raspberry Pi อย่างมาก
- เนื่องจากเจาะกลุ่มเจ้าของอุปกรณ์ Apple ที่ไม่ต้องการ compromise ระบบปฏิบัติการของตัวเอง จึงมองว่ากลุ่มผู้ใช้งานยิ่งแคบลง
YouTube Premium และการทดลองต้นทุนโฆษณา
- ผู้เขียนมองว่ายังไม่แน่ใจว่า YouTube Premium ที่ราคา CAD $9.99/mo หรือ CAD $11.99/mo และประมาณ CAD $13.43/mo เมื่อรวมภาษี จะถือว่าสมเหตุสมผลหรือไม่
- มีการทดลองดูการแสดงโฆษณาเป็นเวลาหนึ่งวันด้วยโน้ตบุ๊กที่สะอาดและ private browsing โดยดู YouTube เป็นช่วง ๆ
- ในประวัติการรับชมมีวิดีโอที่ “ดู” เพียง 10 รายการ
- ระหว่างดูบางส่วนของวิดีโอ 10 รายการนั้น พบโฆษณา 8 ชิ้น
- มีเพียง 2 ชิ้นที่กดข้ามได้ และก็ข้ามทั้งคู่
- หากสมมติ CPV คร่าว ๆ ที่ USD $0.15 โฆษณา 8 ชิ้นต่อวันจะเท่ากับต้นทุนฝั่งผู้ลงโฆษณา
8 x $0.15 = $1.20และเมื่อนำไปคาดการณ์ทั้งเดือนจะอยู่ที่ราว USD $36/mo - ยังมีการคำนวณโดยอิงข้อมูล Statista ด้วยการนำยอดใช้จ่ายโฆษณาในสหรัฐฯ หารด้วยจำนวนการดูทั้งหมด
- ในปี 2019 ผู้ลงโฆษณาในสหรัฐฯ ใช้เงินกับ YouTube ไป $15.1 billion
- ผู้อยู่อาศัยในสหรัฐฯ รับชมวิดีโอ 916 billion ครั้ง
- ค่าเฉลี่ยคือ
$15.1B / 916B = USD $0.0165 per view - สำหรับกรณีของผู้เขียน คำนวณได้ว่าเป็นต้นทุนโฆษณาประมาณ USD $0.13 ต่อวัน และราว USD $3.96 ต่อเดือน
- ระหว่างการทดลองโฆษณา ผู้เขียนปิดเสียงด้วยฮาร์ดแวร์และละสายตาออกบ่อยครั้ง จึงมองว่าเงินโฆษณาที่ลงกับตนเองนั้นสูญเปล่า
- ถึงอย่างนั้นก็ยังต้องการสนับสนุนครีเอเตอร์ และจะลองใช้ Premium trial 3 เดือน พร้อมกับติดตามต่อไปว่า Google ติดตามข้อมูลอะไรเกี่ยวกับตนบ้าง
- ผู้เขียนกังวลว่าเมื่อมีการยื่น DMCA claim รายได้จากโฆษณาทั้งหมดอาจไม่ไปถึงครีเอเตอร์แต่ไปถึงผู้เรียกร้องสิทธิ์แทน และเสริมว่าไม่แปลกที่ครีเอเตอร์จำนวนมากย้ายไป Patreon
สรุปท้ายสุด
- ตั้งค่าฮาร์ดแวร์เราเตอร์ใหม่ตั้งแต่ต้น และแยก LAN ออกเป็น trusted/untrusted zone
- ตั้งค่าการบล็อกโฆษณาแบบ DNS ดั้งเดิม
- เพิ่ม transparent MITM proxy
- สุดท้ายสามารถบล็อกโฆษณา YouTube บนอุปกรณ์ Apple ที่เชื่อมต่อเครือข่ายได้อย่างมีประสิทธิภาพ
- ผู้เขียนบอกว่าส่วนที่ยากจบแล้ว จึงจะพิจารณาจ่าย YouTube Premium แต่เสริมว่า tracker ยังคงถูกบล็อกอย่างหนักเหมือนเดิม
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ดูเหมือนว่าไม่ใช่ข้อบกพร่องของรูปแบบ Protobuf แต่เป็นการที่ผู้เขียนเปลี่ยนหมายเลขฟิลด์ไปเป็นหมายเลขใหญ่ ๆ ที่ยังไม่ได้ใช้
วิธีคือค้นหาลายเซ็น URL โฆษณาอย่าง
/pagead/ในไบต์ของ Protobuf เพื่อจับช่วงของฟิลด์ แล้วไล่ย้อนกลับไปหาทั้งแท็กฟิลด์เป้าหมายและคีย์ฟิลด์เพื่อทำให้ใช้งานไม่ได้ แต่นี่ใกล้เคียงกับ พฤติกรรมที่ตั้งใจไว้ มากกว่าจะเป็นข้อบกพร่องถ้าจะลงแรงพอที่จะหาแท็กได้อยู่แล้ว การอ่านความยาว varint ที่อยู่ข้าง ๆ แล้วข้ามไบต์ส่วนนั้นไปก็ไม่ใช่งานเพิ่มที่ใหญ่นัก อาจต้องคัดลอกบัฟเฟอร์หรือเลื่อนไบต์ แต่สคริปต์ PoC เองก็ต้องคัดลอกอยู่แล้ว เพราะ
bytesที่ API ของ mitmproxy คืนมาเป็นแบบแก้ไขไม่ได้ก่อนที่ Google จะเปลี่ยนโปรโตคอลถึงขั้นทำให้แอปเวอร์ชันเดิมไม่มีโฆษณาเลย ก็คงปล่อยแอปใหม่ออกมาก่อน ดังนั้นแค่ใช้การตรึงใบรับรองพื้นฐาน หรือถอดรหัสแบบไม่ผ่อนปรนต่อความล้มเหลวในการดึงข้อมูลโฆษณา ก็สามารถปิดวิธีบล็อกนี้ได้ทันที ถ้าเป็นทีม YouTube ก็น่าจะมองจุดนี้ว่าเป็นข้อบกพร่อง
bytesแก้ไขไม่ได้ แต่bytearrayไม่ได้แก้ไขไม่ได้ใช้ พร็อกซี C++/Go ขนาดเล็กก็ทำงานเดียวกันได้โดยมีโอเวอร์เฮดน้อยกว่ามาก งานที่นิยามชัดเจนแบบนี้เสถียรกว่าและยุ่งยากน้อยกว่าการปล้ำกับ mitmproxy
ถ้าส่งทราฟฟิกทั้งหมดผ่านพร็อกซี ประสิทธิภาพจะตกแม้จะใช้การดัก SNI ก็ตาม pfSense ก็เช่นกัน ถ้ามีเซิร์ฟเวอร์ Linux ธรรมดากับกฎ iptables ง่าย ๆ ก็จัดการได้โดยไม่ต้องสู้กับชั้น abstraction ของ pfSense
เขียนเฉพาะฟิลด์ proto ที่จำเป็นจากที่ reverse engineer มาไว้ในไฟล์
.protoแล้วสร้างโค้ดอัตโนมัติ จากนั้นเปลี่ยนแฟล็กก็พอ ถูกกว่าการทำด้วย Python และอัปเดตง่ายกว่าเมื่อ proto เปลี่ยน การเมินแท็กฟิลด์ที่ไม่รู้จักเป็นฟีเจอร์สำคัญของ Protobuf และช่วยให้เปลี่ยนสคีมาที่เข้ากันได้โดยไม่ทำให้ deployment เดิมพังดูเหมือนผู้เขียนบทความจะรับรู้ประเด็นส่วนใหญ่ในคอมเมนต์นี้อยู่แล้ว และบทความก็ค่อนข้างละเอียด เขา benchmark ด้วย Python และ C++ แล้ว implementation สุดท้ายก็ไม่ได้ decode Protobuf ด้วยซ้ำ เขายังลองใช้โซลูชัน mitm หลายแบบแล้ว และใช้ pfSense ไม่ใช่เพราะเป็นแค่เราเตอร์ความปลอดภัยธรรมดา แต่เพื่อเล็งเฉพาะทราฟฟิกของ Apple TV ด้วย VLAN และ VPN
คอมเมนต์นี้รู้สึกถูกและดูแคลนเกินไป บทความต้นฉบับไม่ได้เป็นแบบนั้น ดังนั้นถ้าจะพูดแบบนี้ ก็ควรพิสูจน์ให้ชุมชนเห็นเอง
ถ้าจ่าย YouTube Premium แล้วถือว่าสนับสนุนครีเอเตอร์ไหม? ถ้าใช่ อยากรู้ว่าเมื่อเทียบกับการสนับสนุนโดยตรงอย่าง Patreon แล้วประมาณไหน
รายได้ที่ครีเอเตอร์แต่ละรายได้จากการสมัคร YouTube Premium หนึ่งบัญชีคงเล็กน้อยมาก แต่ก็ยังดีกว่าการดูวิดีโอขณะบล็อกโฆษณา
คิดตาม เวลาในการรับชม ไม่ใช่ impression ของโฆษณา ดังนั้นครีเอเตอร์ที่ทำคอนเทนต์แบบยาวจะได้เปรียบกว่า
บัญชี YouTube ของแฟนผมแปลกตรงที่ไม่ว่าจะล็อกอินจากอุปกรณ์ไหนก็ไม่มีโฆษณา รวมถึง Apple TV ด้วย ไม่ใช่ Premium และไม่เคยเป็น Premium มาก่อน
สงสัยว่าภายในมีแฟล็กอะไรตั้งไว้จนปิดโฆษณาหรือเปล่า
ตอนนั้นเองถึงได้มีช่วงที่เข้าใจว่าทำไมผู้คนถึงบ่นกัน
แม้ไม่ได้ใช้ตัวบล็อกโฆษณา แต่แค่ล็อกอินก็ไม่เห็นโฆษณาเลยทั้งบนเว็บไซต์และแอปมือถือ ไม่มี Twitch Turbo และไม่มี Amazon Prime แล้วด้วย สิทธิประโยชน์ Turbo อื่น ๆ ก็ไม่มี จึงไม่ได้ถูกแฟล็กเป็น Turbo ทั้งหมดแน่ ๆ
ไม่รู้ว่าเมื่อก่อนตอนทำ bug bounty แล้วไปแตะโน่นนี่จนโปรไฟล์บัญชีพังโดยบังเอิญหรือเปล่า แต่ถ้าจะปล่อยให้ผมได้ประโยชน์นี้ต่อ ผมก็ให้ข้อมูลละเอียดกว่านี้ได้
ที่แปลกคือผมจำได้ว่าเมื่อก่อนตอนอยู่โรงพยาบาล เมายาและเจ็บปวดจนอยากดูแค่ทีวี แต่โฆษณา Twitch หนักมากจนแทบสติแตก แล้วอีก 1–2 ปีต่อมาจู่ ๆ ก็รู้ตัวว่าไม่ได้เห็นโฆษณามาหลายปีแล้ว
น่าจะมี A/B test แบบไม่มีโฆษณา ที่ถูกลืมไปนาน และไม่คุ้มที่จะเก็บกวาด เลยยังค้างอยู่ ผมจึงได้ประโยชน์มาหลายปีและดู Twitch มากกว่าแพลตฟอร์มอื่นใด Turbo ของ Twitch ในสหราชอาณาจักรราคาเดือนละ £12 หรือราว $15.50 ซึ่งแพงแม้เทียบทั่วโลก และเมื่อเทียบกับ $12/€12 ในสหรัฐฯ และยุโรป ก็ถือว่าเป็นราคาที่เสียเปรียบพอสมควร
การวางพร็อกซีแบบคนกลางไว้ระหว่าง Apple TV กับอินเทอร์เน็ตภายนอกแล้วสามารถถอดรหัสทราฟฟิก HTTPS ได้นั้นค่อนข้างน่าประหลาดใจ
ปกติคิดว่ามันไม่น่าจะทำงานได้ แต่ภายหลังก็แปลกใจอีกเมื่อรู้ว่าสามารถเพิ่ม CA เข้าไปในที่เก็บใบรับรองของ Apple TV ได้ เป็นบทความที่ละเอียดซึ่งไล่ดูทั้งสแตก
เช่น ในมหาวิทยาลัย ถ้าจะให้อุปกรณ์เชื่อมต่อ Wi‑Fi ได้ ต้องนำ MAC address ไปใส่ allowlist หรือติดตั้งใบรับรอง
แต่ถ้าทำแบบนั้น YouTube อาจพังในสภาพแวดล้อมองค์กรจำนวนมาก จึงไม่แน่ใจว่าจะทำจริงไหม ถึงอย่างนั้น น่าเสียดายที่การปิดกั้นทำได้ง่ายมาก
เคยพยายามทำบน Apple TV อยู่สองสามครั้ง แต่ไม่เคยสำเร็จเลย เหมือนว่า YouTube จะใส่การตรึงใบรับรองลงในแอปแล้วหรืออะไรทำนองนั้น อยากรู้ว่าช่วงนี้มีใครทำให้มันทำงานได้ไหม
[0] https://frida.re/docs/home/
ชอบความพยายามทุกแบบในการบล็อกทั้งเครือข่ายสำหรับบริการออนไลน์ห่วย ๆ ที่ถูกยัดเยียดให้ใช้
การบล็อกโฆษณาก็ดี แต่ถ้ามีวิธีที่ง่ายและแพร่หลายกว่านี้ในการบล็อก infinite scroll เชิงรุกอย่าง YouTube Shorts หรือ Instagram Reels ทั้งเครือข่ายก็คงดี
ใน Instagram แค่อยากดูโพสต์กับสตอรีของคนที่ติดตาม ไม่อยากให้มันแนะนำวิดีโองี่เง่าที่ออกแบบมาเพื่อดึงความสนใจ อาจเป็นการเผยให้เห็นว่าขาดวินัยก็ได้ แต่บ่อยครั้งก็เผลอดูไปสองสามคลิปแล้วเสียเวลาในชีวิตไป 15 นาที
ผู้ใช้อินเทอร์เน็ตโดยรวมเลือกทางที่ไม่อยากจ่ายเงิน ดังนั้นจึงต้องมีใครสักคนรับต้นทุน โดยภาพรวมแล้ว ผู้ใช้อินเทอร์เน็ตไม่ได้ตอบแทนฝั่งที่ไม่แสดงโฆษณา อยากได้คอนเทนต์ แต่โดยมากอยากได้ฟรี
เจอสคริปต์ที่เปลี่ยนหน้า Instagram ให้เหมือนเป็นแค่แท็กรูปภาพ เพื่อให้ดูได้เฉพาะรูป: https://greasyfork.org/en/scripts/5014-un-instagram
ดังนั้นดูเหมือนไม่ใช่แค่ขาดวินัย แต่ใกล้เคียงกับความชาชินที่เราสะสมมามากกว่า และนั่นค่อนข้างแย่ ความพยายามและความสร้างสรรค์ในการทำให้เรากลับมาเป็นฝ่ายใช้แพลตฟอร์ม แทนที่จะให้แพลตฟอร์มทำให้เราใช้งานนั้นน่ายกย่อง
คุยกับเด็ก ๆ เป็นประจำ และพวกเขาก็เห็นด้วยว่ามันเป็นอันตราย แต่ต้านทานได้ยากมาก แม้แต่ตัวเองก็ยังถูกดึงไป doomscrolling เป็นครั้งคราว
ที่ไหนทำได้ก็ตั้งค่า Pi-hole เพื่อกรองโฆษณาไว้แล้ว แต่ไม่อยากบล็อก YouTube ทั้งหมด ถึงอย่างนั้น เพื่อปกป้องครอบครัว ต่อไปคงต้องพิจารณาอย่างจริงจัง
งานวิศวกรรมดี แต่ก็น่าเศร้านิดหน่อยที่ต้องทำถึงขนาดนี้เพื่อให้ใช้งานฮาร์ดแวร์หรือซอฟต์แวร์ของตัวเองราวกับว่าเป็นเจ้าของได้บ้าง
YouTube มีโฆษณาด้วยเหรอ? เบราว์เซอร์บล็อกให้ดีเกินไปจนไม่รู้เลย
ปัญหาจริงคือประสบการณ์บน Apple TV แย่กว่าประสบการณ์บนเว็บเบราว์เซอร์ทั่วไปมาก Apple ล็อกฮาร์ดแวร์ไว้แน่นเกินไป จนโครงสร้างกลายเป็นเอื้อต่อรายได้โฆษณาของ YouTube มากกว่าผู้บริโภคปลายทางที่จ่ายเงินซื้อมา
ตอนท่องเว็บด้วย iPad นอกเครือข่าย Pi-hole ที่บ้านก็เหมือนกัน ไม่รู้ว่าคนอื่นทนสิ่งนี้ทุกวันได้อย่างไร
iPad เป็นอุปกรณ์ที่ที่ทำงานแจกให้ จึงไม่ค่อยใช้ส่วนตัว แต่ทุกครั้งที่ใช้ก็ถูกเตือนว่ามันน่ารำคาญแค่ไหน
แปลกตรงที่ก่อนจะได้ iPad คิดว่ามันคงมีประโยชน์แค่สำหรับเสพคอนเทนต์ แต่จริง ๆ แล้วมันสะดวกมากสำหรับการรีโมตเข้าใช้งานทรัพยากรงานอย่างรวดเร็ว ส่วนการท่องเว็บทั่วไปกับสตรีมมิงมีเดียกลับเป็นอุปกรณ์ที่ติดอยู่ในแดนรกร้างที่เต็มไปด้วยโฆษณา
ถ้าต้องการ YouTube แบบไม่มีโฆษณา ใช้ https://yewtu.be หรืออินสแตนซ์ Invidious อื่น ๆ https://docs.invidious.io/instances/ ได้
ระหว่าง YouTube กับ Invidious มีการแข่งขันสะสมอาวุธกันอยู่ และบางครั้ง Invidious ก็ใช้งานไม่ได้ แต่ทีมก็มักหาวิธีใหม่ ๆ ในการเลี่ยง YouTube และส่งวิดีโอแบบไม่มีโฆษณาได้เสมอ