diff --git a/README.md b/README.md index d1e9384..d1fb474 100644 --- a/README.md +++ b/README.md @@ -4,62 +4,40 @@ ## 기능 -### 1. 그리드 우회 (1080p 시청) +### 그리드 우회 (1080p 시청) `navigator.userAgent`, `navigator.platform`, `navigator.userAgentData` 를 Mac 으로 위장해, 치지직이 윈도우에서 1080p 시청 시 요구하는 그리드(Grid) 설치 없이 1080p 화질을 시청할 수 있게 합니다. +`userAgentData` 는 단순히 `brands` / `mobile` / `platform` 만 갈아끼우면 +치지직이 로드하는 `nlog.js` (네이버 핑거프린팅 / 로깅) 가 +`e.getHighEntropyValues is not a function` 으로 Uncaught TypeError 를 던집니다. +이 자체가 비정상 접근 시그널이 될 수 있어서, `getHighEntropyValues` 메서드도 +일관된 Mac 값으로 채워 둡니다. (manifest 의 `world: MAIN` + `run_at: document_start` +덕분에 페이지 스크립트보다 먼저 적용됩니다.) + 구현: `content.js` -### 2. 타임머신 강제 활성화 (되감기 UI, cosmetic) +## 타임머신 강제 활성화는 지원하지 않습니다 -스트리머가 타임머신을 끄고 방송 중인 라이브에서도 플레이어의 되감기 (seek) 바 -UI 를 띄우려 시도합니다. +이전 버전(`1.1.x`)에는 스트리머가 타임머신을 꺼둔 라이브에서도 되감기 바 UI 를 +띄우는 기능이 있었습니다. **`1.2.0` 부터는 제거되었습니다.** -동작 방식: +이유: -1. `api.chzzk.naver.com/service/v3.2/channels/{id}/live-detail` 응답을 - `window.fetch` 와 `XMLHttpRequest` 양쪽에서 가로챕니다. 치지직 React 앱은 - axios 기반이라 실제 요청이 XHR 로 나가므로 XHR 후킹이 필수입니다. -2. 응답 본문의 `content.timeMachineActive`, `content.timeMachinePlayback` - 두 플래그를 `true` 로 만듭니다. -3. 내부의 `livePlaybackJson` (JSON 문자열) 을 파싱해서 `meta.liveRewind = true`, - `meta.duration` 보강, `live.timeMachine = true` 를 주입한 뒤 다시 직렬화합니다. - 이 필드들이 플레이어가 seek 바 UI 를 켤지 결정하는 트리거입니다. +- 치지직의 DVR window 는 스트리머가 타임머신을 켰을 때만 CDN 이 + 프로비저닝합니다. 클라이언트에서 `timeMachineActive` 플래그만 `true` 로 + 바꿔도 HLS 매니페스트(`timemachine=false`) 와 상태가 어긋나서 실제 되감기는 + 동작하지 않습니다 (cosmetic 한 UI 만 뜸). +- 더 심각한 문제는, 이 클라이언트-사이드 상태 불일치가 치지직의 비정상 접근 + 감지 로직을 트리거해 **"허용되지 않는 비정상적 접근입니다. 반복적인 접근 시 + 운영 정책에 따라 조치될 수 있습니다."** 팝업이 뜨는 것이 실측으로 확인되었습니다. + 계정 제재 위험이 있어 기능을 제거했습니다. -> **중요한 한계 (반드시 읽으세요)** -> -> 이건 **UI 만 켜는 cosmetic 패치** 입니다. 치지직의 DVR window 는 스트리머가 -> 타임머신을 켰을 때만 CDN 이 프로비저닝하기 때문에, 스트리머가 꺼둔 라이브의 -> HLS 매니페스트에는 과거 시점의 segment 자체가 존재하지 않습니다. 따라서: -> -> - **재생바가 떠도 실제 되감기는 동작하지 않을 가능성이 매우 높습니다** (누르면 -> live edge 로 튕기거나 segment not found 가 뜸). -> - 진짜 되감기를 살리려면 CDN 이 만들어 둔 DVR 매니페스트가 있어야 하는데, -> `service/v1/channels/{id}/live-playback-json` 엔드포인트는 스트리머가 타임머신을 -> 켜둔 채널에서만 200 을 돌려주고 꺼둔 채널에서는 404 입니다 (실측 확인). -> -> 참고: -> [Streamlink chzzk 플러그인](https://github.com/streamlink/streamlink/blob/master/src/streamlink/plugins/chzzk.py) -> 도 `timeMachineActive` 가 true 일 때만 `live-playback-json` 을 부릅니다. -> [Choonholic/ChzzkDownloader](https://github.com/Choonholic/ChzzkDownloader) -> 의 `--stream force-timemachine` 옵션도 마찬가지로 서버-사이드 (CORS 무관) -> 에서 동일 엔드포인트를 강제로 두드릴 뿐, 서버가 DVR 을 안 만들었다면 똑같이 -> 실패합니다. - -구현: `timemachine.js` - -#### 진단 모드 - -응답 구조를 직접 들여다보고 싶으면 devtools 콘솔에서: - -```js -localStorage.setItem('chzzk-bypass:debug', '1') -``` - -치고 새로고침하면 `[chzzk-bypass:timemachine] DEBUG live-detail content after patch: {...}` -로 패치된 content + `livePlaybackJson` 파싱 결과가 통째로 찍힙니다. +스트리머가 끈 타임머신은 클라이언트 확장만으로는 안전하게 우회할 수 없습니다. +되감기가 필요하면 스트리머에게 타임머신을 켜달라고 요청하거나 다시보기 +업로드를 기다려 주세요. ## 설치 @@ -73,21 +51,12 @@ localStorage.setItem('chzzk-bypass:debug', '1') ``` manifest.json 확장 정의 (MV3) -content.js 그리드 우회 (UA / platform / UA-CH 위장) -timemachine.js 타임머신 강제 활성화 (fetch + XHR 후킹) +content.js 그리드 우회 (UA / platform / UA-CH 위장 + getHighEntropyValues) +rules.json declarativeNetRequest 규칙 (현재 비어 있음) popup.html/css/js 툴바 팝업 icons/ 확장 아이콘 ``` -## 동작 확인 - -브라우저 devtools 콘솔에서 다음 로그를 확인할 수 있습니다. - -- `[chzzk-bypass:timemachine] hooks installed (fetch + XHR)` — 후킹 성공 -- `[chzzk-bypass:timemachine] XHR live-detail intercepted for ` — XHR 가로채기 진입 -- `[chzzk-bypass:timemachine] forcing timeMachine ON for ` — 패치 적용 -- `[chzzk-bypass:timemachine] inner livePlaybackJson meta/live patched (liveRewind, duration, timeMachine)` — 내부 매니페스트 메타 패치 성공 - ## 면책 본 확장은 학습 및 개인 사용 목적의 우회 도구입니다. 사용에 따른 모든 책임은 diff --git a/content.js b/content.js index 496aa4d..17c032d 100644 --- a/content.js +++ b/content.js @@ -1,26 +1,82 @@ -// 치지직 플레이어가 OS를 검사할 때 Mac으로 인식하도록 속임 -Object.defineProperty(navigator, 'userAgent', { - get: function () { - return 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'; - } -}); - -Object.defineProperty(navigator, 'platform', { - get: function () { - return 'MacIntel'; - } -}); - -Object.defineProperty(navigator, 'userAgentData', { - get: function () { - return { - brands: [ - { brand: "Chromium", version: "122" }, - { brand: "Google Chrome", version: "122" }, - { brand: "Not-A.Brand", version: "24" } - ], - mobile: false, - platform: "macOS" - }; - } -}); \ No newline at end of file +// 치지직이 윈도우에서 1080p 시청 시 그리드 설치를 요구하는 것을 우회하기 위해 +// navigator 의 OS/브라우저 식별 값들을 Mac 으로 위장한다. +// +// userAgentData 의 getHighEntropyValues 까지 구현해 두지 않으면 +// 치지직이 로드하는 nlog.js (네이버 핑거프린팅 / 로깅) 가 +// `e.getHighEntropyValues is not a function` 으로 Uncaught TypeError 를 던지고, +// 그 자체가 비정상 접근 시그널이 될 수 있다. 그래서 객체 형태와 메서드까지 +// 일관된 Mac 값으로 채워 둔다. (manifest 의 world: MAIN + run_at: document_start +// 덕분에 페이지 스크립트보다 먼저 적용된다.) + +Object.defineProperty(navigator, 'userAgent', { + get: function () { + return 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'; + } +}); + +Object.defineProperty(navigator, 'platform', { + get: function () { + return 'MacIntel'; + } +}); + +const __chzzkBypassBrands = [ + { brand: 'Chromium', version: '122' }, + { brand: 'Google Chrome', version: '122' }, + { brand: 'Not-A.Brand', version: '24' } +]; + +const __chzzkBypassFullVersionList = [ + { brand: 'Chromium', version: '122.0.0.0' }, + { brand: 'Google Chrome', version: '122.0.0.0' }, + { brand: 'Not-A.Brand', version: '24.0.0.0' } +]; + +const __chzzkBypassHighEntropy = { + brands: __chzzkBypassBrands, + mobile: false, + platform: 'macOS', + platformVersion: '10.15.7', + architecture: 'x86', + bitness: '64', + model: '', + uaFullVersion: '122.0.0.0', + fullVersionList: __chzzkBypassFullVersionList, + wow64: false, + formFactors: [] +}; + +const __chzzkBypassUAData = { + brands: __chzzkBypassBrands, + mobile: false, + platform: 'macOS', + getHighEntropyValues: function (hints) { + // 항상 brands / mobile / platform 은 포함하고, 요청된 hints 만 추가로 채운다. + const out = { + brands: __chzzkBypassBrands, + mobile: false, + platform: 'macOS' + }; + if (Array.isArray(hints)) { + for (const h of hints) { + if (Object.prototype.hasOwnProperty.call(__chzzkBypassHighEntropy, h)) { + out[h] = __chzzkBypassHighEntropy[h]; + } + } + } + return Promise.resolve(out); + }, + toJSON: function () { + return { + brands: __chzzkBypassBrands, + mobile: false, + platform: 'macOS' + }; + } +}; + +Object.defineProperty(navigator, 'userAgentData', { + get: function () { + return __chzzkBypassUAData; + } +}); diff --git a/manifest.json b/manifest.json index 0255071..73cc011 100644 --- a/manifest.json +++ b/manifest.json @@ -1,45 +1,44 @@ -{ - "name": "Chzzk Bypass", - "version": "1.1.3", - "manifest_version": 3, - "description": "치지직(CHZZK) 시청 환경 개선: 1) Mac 위장으로 그리드 없이 1080p 시청, 2) 스트리머가 타임머신을 꺼둔 라이브에서도 되감기 UI 강제 표시.", - "icons": { - "16": "icons/icon16.png", - "48": "icons/icon48.png", - "128": "icons/icon128.png" - }, - "action": { - "default_popup": "popup.html", - "default_title": "Chzzk Bypass Status" - }, - "permissions": [ - "declarativeNetRequest", - "declarativeNetRequestWithHostAccess" - ], - "host_permissions": [ - "*://*.naver.com/*", - "*://*.ncloud.com/*" - ], - "declarative_net_request": { - "rule_resources": [ - { - "id": "rules", - "enabled": true, - "path": "rules.json" - } - ] - }, - "content_scripts": [ - { - "matches": [ - "*://chzzk.naver.com/*" - ], - "js": [ - "content.js", - "timemachine.js" - ], - "run_at": "document_start", - "world": "MAIN" - } - ] -} \ No newline at end of file +{ + "name": "Chzzk Bypass", + "version": "1.2.0", + "manifest_version": 3, + "description": "치지직(CHZZK) 시청 환경 개선: Mac 위장으로 그리드 없이 1080p 시청.", + "icons": { + "16": "icons/icon16.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + }, + "action": { + "default_popup": "popup.html", + "default_title": "Chzzk Bypass Status" + }, + "permissions": [ + "declarativeNetRequest", + "declarativeNetRequestWithHostAccess" + ], + "host_permissions": [ + "*://*.naver.com/*", + "*://*.ncloud.com/*" + ], + "declarative_net_request": { + "rule_resources": [ + { + "id": "rules", + "enabled": true, + "path": "rules.json" + } + ] + }, + "content_scripts": [ + { + "matches": [ + "*://chzzk.naver.com/*" + ], + "js": [ + "content.js" + ], + "run_at": "document_start", + "world": "MAIN" + } + ] +} diff --git a/popup.html b/popup.html index 1a5678f..ab58e47 100644 --- a/popup.html +++ b/popup.html @@ -21,10 +21,6 @@ 그리드 우회 Mac 위장 (1080p) -
- 타임머신 - 강제 활성화 -