Emby 全平台全版本通杀hook代码 解锁所有限制
在本地手机/电脑上已测试通过。此方案通杀iOS/MacOS/Linux/Android 全平台,只要你找得到apploader加载我的代码就可以bypass,因为用的原理是js原生api拦截。
我发现网上这些搞Emby Server / Client 破解的一个个都只会dllspy然后提取资源 替换网址 然后mitm或者自己搭建一个api接口。
而且大部分还是误人子弟,瞎改。进而还衍生出所谓的B站大佬发点修改后的Emby Apk 言语之际居然比我还狂-----如果改改html也是大佬的话。
原理: 利用html初始化后的事件进行jsvm层的api劫持。
2024.03.08 全平台通杀代码无需反编译 by QiuChenly
[color=]/
[color=]Applications
[color=]/
[color=]EmbyServer
[color=].
[color=]app
[color=]/
[color=]Contents
[color=]/
[color=]Resources
[color=]/
[color=]dashboard
[color=]-
[color=]ui
[color=]/
[color=]apploader
[color=].
[color=]js
[color=]
[color=]function
[color=]
[color=]onDone
[color=](): 函数开头加入以下拦截代码
[color=]
[color=]// 重写全局的 fetch 函数
[color=]
[color=]//powered by QiuChenly use node module hook
[color=] (
[color=]window
[color=].
[color=]fetch1
[color=] =
[color=]window
[color=].
[color=]fetch
[color=]),
[color=] (
[color=]window
[color=].
[color=]fetch
[color=] = (
[color=]url
[color=],
[color=]options
[color=])
[color=]=>
[color=] {
[color=]
[color=]console
[color=].
[color=]log
[color=](
[color=]"加载的URL是:"
[color=],
[color=]url
[color=]);
[color=]
[color=]//如果url 包含 https://mb3admin.com/admin/service/registration/validateDevice 则直接返回
[color=]
/*
{
[color=] status: 200,
[color=] headers: $response.headers,
[color=] body: '{"cacheExpirationDays":999,"resultCode":"GOOD","message":"Device Valid"}'
[color=] }
[color=] */
[color=]
[color=]if
[color=] (
[color=]
[color=]url
[color=] ===
[color=]
[color=]"[color=#d4d4d4">https://mb3admin.com/admin/service/registration/getStatus"
||
[color=]
[color=]url
[color=].
[color=]includes
[color=](
[color=]
"https://mb3admin.com/admin/service/registration/validateDevice"
[color=] )
[color=] ) {
[color=]
[color=]return
[color=]
[color=]new
[color=]
[color=]Promise
[color=]((
[color=]resolve
[color=],
[color=]reject
[color=])
[color=]=>
[color=] {
[color=]
[color=]resolve
[color=]({
[color=]
[color=]status:
[color=]
[color=]200
[color=],
[color=]
[color=]headers:
[color=] {
[color=]
[color=]get
[color=]:
[color=] ()
[color=]=>
[color=]
[color=]"application/json"
[color=],
[color=] },
[color=]
[color=]json
[color=]:
[color=] ()
[color=]=>
[color=] {
[color=]
[color=]return
[color=] {
[color=]
[color=]cacheExpirationDays:
[color=]
[color=]999
[color=],
[color=]
[color=]resultCode:
[color=]
[color=]"GOOD"
[color=],
[color=]
[color=]message:
[color=]
[color=]"Device Valid"
[color=],
[color=]
[color=]// 上半部分是分开的
[color=]
[color=]deviceStatus:
[color=]
[color=]0
[color=],
[color=]
[color=]planType:
[color=]
[color=]"超级会员"
[color=],
[color=]
[color=]subscriptions:
[color=] [
[color=] {
[color=]
[color=]autoRenew:
[color=]
[color=]true
[color=],
[color=]
[color=]store:
[color=]
[color=]"秋城落叶"
[color=],
[color=]
[color=]feature:
[color=]
[color=]"all"
[color=],
[color=]
[color=]planType:
[color=]
[color=]"超级会员"
[color=],
[color=]
[color=]expDate:
[color=]
[color=]"直到2099年12月31日以后"
[color=],
[color=] },
[color=] ],
[color=] };
[color=] },
[color=] });
[color=] });
[color=] }
[color=]
[color=]return
[color=]
[color=]window
[color=].
[color=]fetch1
[color=](
[color=]url
[color=],
[color=]options
[color=]);
[color=] });
示例App获取地址: https://github.com/MediaBrowser/Emby.Releases/releases/download/4.7.14.0/embyserver-osx-x64-4.7.14.0.zip
1. 初步分析
在设置中有一个专业版订阅,我们看下:
16979891768628.jpg (71.75 KB, 下载次数: 0)
下载附件
2023-10-23 00:17 上传
点击保存我们发现
16979891539758.jpg (781.94 KB, 下载次数: 0)
下载附件
2023-10-23 00:17 上传
https://mb3admin.com/admin/service/registration/validateDevice?xxxx 后面是随机生成的不可逆的hash数据,所以我没打码。
这个app我们打开,可以看到非常怀疑是前端页面资源直接返回到浏览器的
16979893108594.jpg (257.71 KB, 下载次数: 0)
下载附件
2023-10-23 00:17 上传
dashboard-ui很明显。
我们用vscode搜索一下看看:
16979893841332.jpg (1.29 MB, 下载次数: 0)
下载附件
2023-10-23 00:17 上传
还真搜索到了,这么容易就破解了吗?我不信。
2. 修改文件不起作用
我们上一步找到的接口文件,如果我们伪造数据返回岂不是就能解锁专业版?我们试试。
16979894868928.jpg (646.11 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
本地我们起了个3000端口服务,看看能不能收到数据包。
保存,重启app,订阅:
16979895451803.jpg (560.07 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
此时神奇的一幕发生了,无论我怎么修改这个js文件的内容,永远不起作用,这是为什么呢?
答案只有一个,这个js文件无论内容是什么都不重要,重要是的这个文件存在就可以了,只要app拦截页面访问请求,在代码中构造一个内嵌的js文件,就可以做到这种效果。那么是哪个文件呢?
当然是我们最爱的暴力搜索辣!
由于dll文件无法被Sublime Text搜索到,我不知道是不是我的设置问题,所以我手动一个个文件放到HexFriends里面搜索字符串:
16979899409029.jpg (2.06 MB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
终于搜索到了。
这个文件是Emby.Web.dll,是C#动态库,那么我们有请windows和dnSpy上场!
16979900927120.jpg (747.13 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
在解析完成后,我们看到资源文件中果然出现了一个同名文件,我们导出这个文件:
16979901284649.jpg (132.18 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
我们放到vscode中格式化后,可以看到这个请求的逻辑是:
16979902341251.jpg (264.56 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
如果这个接口走通了,那么直接
return (
appStorage.setItem(
cacheKey,
JSON.stringify({
lastValidDate: Date.now(),
deviceId: params.deviceId,
cacheExpirationDays: response.cacheExpirationDays,
lastUpdated: Date.now(),
})
),
Promise.resolve()
);
那么我们直接返回这个数据不就得了?
于是我插入了这一段修改后的代码:
16979903155309.jpg (170.64 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
其作用是伪造了服务器返回的数据,让他走逻辑成功。
最后复制保存为js文件,切记文件名要和导出的文件名一致,重新把这个文件导入进dll中:
16979904106087.jpg (47.07 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
在弹出的窗口中选择自己保存的同名文件即可。
最后保存文件修改:
16979904510273.jpg (29.21 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
我这里保存过了,所以“全部保存”是灰色的,修改后一定要选择全部保存后才会对dll进行实际修改哦!
保存文件后替换app里的同名文件试试:
16979905544981.jpg (335.03 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
打开app我们在再试一遍:
16979906171320.jpg (947.02 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
这里打开f12后一定要长按左键清空浏览器缓存刷新,否则js文件可能不是最新的。
但是我们发现重新输入后这里好像没有发生变化,依然是让你订阅并告诉你没有有效订阅。
我们定位到/Applications/EmbyServer.app/Contents/Resources/dashboard-ui/embypremiere/embypremiere.js文件看一下,同样是暴力搜索这里不再赘述:
16979908550006.jpg (468.71 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
我们注意到这里有一个IsMBSupporter, 这好像是一个非常关键的词。
16979907884172.jpg (325.74 KB, 下载次数: 0)
下载附件
2023-10-23 00:18 上传
也就是说,如果IsMBSupporter不为true,那么所有逻辑是不会往下走的。于是就显示出了“无效的订阅”字样,我们试着改一下:
16979910514168.jpg (680.67 KB, 下载次数: 0)
下载附件
2023-10-23 00:19 上传
30行我们强制设置为true,并且下面伪造了一个http数据返回,让他正确读取到订阅信息。
我们保存,重新打开app看一下:
16979911138582.jpg (757.93 KB, 下载次数: 0)
下载附件
2023-10-23 00:19 上传
还是一样 清空页面缓存强制刷新,点击保存按钮后可以看到直接就出现了付费版订阅。
至此,破解完毕,只需要修改一个js文件和一个dll文件中的js资源文件即可。
总结
return (
appStorage.setItem(
cacheKey,
JSON.stringify({
lastValidDate: Date.now(),
deviceId: params.deviceId,
cacheExpirationDays: response.cacheExpirationDays,
lastUpdated: Date.now(),
})
),
Promise.resolve()
);
return new Promise((resolve) =>
resolve({
deviceStatus: 0,
planType: "超级会员",
subscriptions: [
{
autoRenew: true,
store: "秋城落叶",
feature: "all",
planType: "超级会员",
expDate: "且会员资格永远不会失效",
},
],
})
);
修改好的文件仅供参考:
https://github.com/QiuChenlyOpenSource/InjectLib/tree/main/EmbyServer