Skip to content

Instantly share code, notes, and snippets.

@all3kcis
Last active October 22, 2024 13:31
Show Gist options
  • Save all3kcis/66909ed95755146a6969b32f21171642 to your computer and use it in GitHub Desktop.
Save all3kcis/66909ed95755146a6969b32f21171642 to your computer and use it in GitHub Desktop.
Emby Theater premiere bypass

/!\ Anyone interested in this Gist should look at the work of @danielchc ;)
-> https://gist.github.com/danielchc/c159626485a08c76856b2d30ae457e04

DNS

/!\ I used bind9 on my local server to add mb3admin.com entry for all devices ( like android app ) Otherwise in hosts fie :

<your_server> mb3admin.com

Optionnal (electron app - Emby Theater) main.js update

C:\Users\<YOUR_USER>\AppData\Roaming\Emby-Theater\system\electronapp
Add after app.on('window-all-closed', function () { block L:18
To accept self certificate

app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
        event.preventDefault()
        callback(true)
})

On your web server

  • Create /var/www/mb3admin.com folder
  • In /var/www/mb3admin.com/ create file index.php with
<?php
if (false){
	$file = 'data.log';
	$content = file_get_contents($file);
	$content .= date('Y-m-d H:i:s').' : '.$_SERVER['REMOTE_ADDR'].' URL : '.$_SERVER['REDIRECT_URL'].' Query : '.json_encode($_REQUEST)."\r\n";
	file_put_contents($file, $content);
}

if ($_SERVER['REDIRECT_URL'] == '/admin/service/registration/validate'){
	header('content-type:application/json');
	$date = new DateTime();
	$date->add(new DateInterval('P14D'));
	echo '{"featId":"'.$_POST['feature'].'","registered":true,"expDate":"'.$date->format('Y-m-d').'","key":"'.$_POST['key'].'"}';
	
}elseif ($_SERVER['REDIRECT_URL'] == '/admin/service/registration/validateDevice'){
	header('content-type:application/json');
	echo '{"cacheExpirationDays":21,"resultCode":"GOOD","message":"Device Valid"}';
}
  • Create file .htaccess
Allow from all Options -Indexes <IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
    Options -MultiViews
</IfModule>
<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "*"
</IfModule>
RewriteEngine On
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L] </IfModule>

Apache site conf

# /etc/apache2/sites-enabled/mb3admin.conf
<VirtualHost *:80>
        DocumentRoot "/var/www/mb3admin.com"
        ServerName mb3admin.com

        <Directory "/var/www/mb3admin.com/">
                Options MultiViews FollowSymlinks

                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
        TransferLog /var/log/apache2/mb3admin_access.log
        ErrorLog /var/log/apache2/mb3admin_error.log
</VirtualHost>

<VirtualHost *:443>
        DocumentRoot "/var/www/mb3admin.com"
        ServerName mb3admin.com

        SSLEngine on
        SSLCertificateFile      /etc/apache2/mb3admin.crt # Self signed certificate
        SSLCertificateKeyFile /etc/apache2/mb3admin.key


        <Directory "/var/www/mb3admin.com/">
                Options MultiViews FollowSymlinks

                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
        TransferLog /var/log/apache2/mb3admin_access.log
        ErrorLog /var/log/apache2/mb3admin_error.log
</VirtualHost>
@Energys018
Copy link

Made this a little easier with the below Dockerfile;

1. Redirect your DNS to point to your docker host

2. Copy the below in to a new file called `Dockerfile`, along with the `index.php`, `.htaccess` and `mb3admin.conf` files as above

3. Build with `docker build -t emby-premiere .`

4. Bring up with `docker run --rm --publish 443:443 -h mb3admin.com --name emby-premiere emby-premiere`

5. Visiting https://mb3admin.com/admin/service/registration/validateDevice should give you a SSL warning. Make sure you install the certificate to trust it system wide (otherwise Emby server will reject it). You should then see "Device Okay".

6. Activate Emby Premiere with any key you want.
FROM php:8-apache-buster

RUN apt-get update && \
    apt-get install -y openssl && \
    openssl genrsa -des3 -passout pass:12345 -out server.pass.key 2048 && \
    openssl rsa -passin pass:12345 -in server.pass.key -out /etc/apache2/mb3admin.key && \
    rm server.pass.key && \
    openssl req -new -key /etc/apache2/mb3admin.key -out server.csr -subj "/C=UK/ST=/L=/O=/OU=/CN=mb3admin.com" && \
    openssl x509 -req -days 365 -in server.csr -signkey /etc/apache2/mb3admin.key -out /etc/apache2/mb3admin.crt && \
    a2enmod ssl && a2enmod rewrite && a2enmod headers && \
    mkdir /var/www/mb3admin.com && chown -R www-data:www-data /var/www/

COPY mb3admin.conf /etc/apache2/sites-enabled/000-default.conf
COPY .htaccess /var/www/mb3admin.com/
COPY index.php /var/www/mb3admin.com/

CMD ["apache2-foreground"]

Hi, i am modificate your Dockerfile

FROM php:8-apache-buster

RUN apt-get update && \
    apt-get install -y openssl && \
    openssl genrsa -des3 -passout pass:12345 -out server.pass.key 2048 && \
    openssl rsa -passin pass:12345 -in server.pass.key -out /etc/apache2/mb3admin.key && \
    rm server.pass.key && \
    openssl req -new -key /etc/apache2/mb3admin.key -out server.csr -subj "/C=UK/ST=/L=/O=/OU=/CN=mb3admin.com" && \
    openssl x509 -req -days 365 -in server.csr -signkey /etc/apache2/mb3admin.key -out /etc/apache2/mb3admin.crt && \
    a2enmod ssl && a2enmod rewrite && a2enmod headers && \
    mkdir /var/www/mb3admin.com && chown -R www-data:www-data /var/www/

ADD mb3admin.conf /etc/apache2/sites-available/mb3admin.conf
ADD .htaccess /var/www/mb3admin.com/.htaccess
ADD index.php /var/www/mb3admin.com/index.php


RUN a2dissite 000-default.conf
RUN a2ensite mb3admin.conf


CMD ["apache2-foreground"]

@morths
Copy link

morths commented Feb 15, 2021

Check if your LGTV admit ssl certificates and stores cookies prior to go to https://mb3admin.com But even if it works, may only be a temporal solution, on my androidTV I must repeat it every few weeks (may be thoses 21 days) En sábado, 13 de febrero de 2021 23:58:55 CET, genfuzuki notifications@github.com escribió: @genfuzuki commented on this gist. You need to block DNS outbound on your router from your LG TV (believe Emby app has hard coded dns check). — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

Nope.. not possible on lg tv. Unfortunately webos isn't as flexible as android i suppose, but thnx anyway!

If any lg tv owner has actually managed to get this done, please advice..

@genfuzuki
Copy link

To be honest I got fed up with my LG TV as it is flakey and apps restart or so does the Tv. It also tends to transcode h265 on my nas rather than direct so I switched to a Fire4k stick and use Jellyfin instead.

@morths
Copy link

morths commented Feb 15, 2021

To be honest I got fed up with my LG TV as it is flakey and apps restart or so does the Tv. It also tends to transcode h265 on my nas rather than direct so I switched to a Fire4k stick and use Jellyfin instead.

Yes i am well aware of jellyfin. In fact i had it installed in the past but switched back to emby due to the absence of webos client (i have invested on lg tvs in my home network). I am checking their page almost every day lately, because i think now they are very close in finally publishing their webos client.. (they are working on it since 2019....... but i am optimistic)

So until then (soon i hope), i am stuck with emby and i decided to give this little trick a try, while waiting.. i thought "who knows? maybe i can keep emby after all", but it was not meant to, as it seems..

@zigazajc007
Copy link

Can't you guys just pay for Emby Premiere lifetime?

@Dracozny
Copy link

Dracozny commented Feb 18, 2021 via email

@zadetek
Copy link

zadetek commented Mar 5, 2021

Can't you guys just pay for Emby Premiere lifetime?

No we can't, not everybody is selling kids minecraft servers.

@zigazajc007
Copy link

zigazajc007 commented Mar 5, 2021

Can't you guys just pay for Emby Premiere lifetime?

No we can't, not everybody is selling kids minecraft servers.

I also sell Minecraft plugins, website, discord bots, database hosting... 😄

@fazalfarhan01
Copy link

fazalfarhan01 commented Apr 17, 2021

Hi there.
I have set up a DNS server which points to the local fake server. I have got emby to show premium on both android and on web version in windows. (Emby theatre doesn't work.)

Android & web

Screenshot_20210417-100806_Emby

But...

  1. No premium feature works on neither the web version nor the android.
  2. Installed podcast plugin which has to have premium to work, but doesn't work. I have set up email notifications and I received this on the email.
Configuration Backup failed

Emby Backup trial has expired. Please purchase Emby Premiere at https://emby.media.
at MBBackup.ServerEntryPoint.ExecuteBackup(BackupProfile settings, CancellationToken cancellationToken, IProgress`1 progress, Boolean isAuto)
at MBBackup.Entities.ScheduledBackupTask.Execute(CancellationToken cancellationToken, IProgress`1 progress)
at Emby.Server.Implementations.ScheduledTasks.ScheduledTaskWorker.ExecuteInternal(TaskOptions options)
  1. Downloads don't work on android.
  2. Live TV from m3u playlists doesn't work.
  3. Emby theater doesn't work either.

And it has this.

The bypass only works to show the premium status in the dashboard, and no premium feature works.

And suggestions why is it happening and how to rectify it? Thanks in advance.😄

EDIT

I see a new request being made to mb3admin.com - "GET /admin/service/registration/getStatus HTTP/1.1"
What must be the response?

Complete Log

192.168.0.102 - - [17/Apr/2021:05:30:41 +0000] "GET /admin/service/registration/validateDevice HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:30:41 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:31:32 +0000] "POST /admin/service/registration/validateDevice?serverId=b2ef47d6e1ee408386179af7a859542c&deviceId=b66c0392-d756-4d56-aba9-7507acb2a417&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:35:04 +0000] "POST /admin/service/registration/validateDevice?serverId=b2ef47d6e1ee408386179af7a859542c&deviceId=b66c0392-d756-4d56-aba9-7507acb2a417&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:35:11 +0000] "POST /admin/service/registration/validateDevice?serverId=b2ef47d6e1ee408386179af7a859542c&deviceId=b66c0392-d756-4d56-aba9-7507acb2a417&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:35:27 +0000] "POST /admin/service/registration/validateDevice?serverId=b2ef47d6e1ee408386179af7a859542c&deviceId=b66c0392-d756-4d56-aba9-7507acb2a417&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:39:19 +0000] "POST /admin/service/registration/validateDevice?serverId=b2ef47d6e1ee408386179af7a859542c&deviceId=b66c0392-d756-4d56-aba9-7507acb2a417&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:39:24 +0000] "POST /admin/service/registration/validateDevice?serverId=b2ef47d6e1ee408386179af7a859542c&deviceId=b66c0392-d756-4d56-aba9-7507acb2a417&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:43:48 +0000] "POST /admin/service/registration/validateDevice?serverId=af798ce4efd04996a15f744dd4b15129&deviceId=d58271c7-8aa8-4ac4-aac4-632752d34002&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:43:48 +0000] "POST /admin/service/registration/validateDevice?serverId=af798ce4efd04996a15f744dd4b15129&deviceId=d58271c7-8aa8-4ac4-aac4-632752d34002&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&embyUserName=fazal&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:44:33 +0000] "POST /admin/service/registration/validateDevice?serverId=af798ce4efd04996a15f744dd4b15129&deviceId=d58271c7-8aa8-4ac4-aac4-632752d34002&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0 HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:44:53 +0000] "POST /admin/service/registration/validate HTTP/1.1" 200 99
192.168.0.102 - - [17/Apr/2021:05:44:53 +0000] "POST /admin/service/registration/validateDevice?serverId=af798ce4efd04996a15f744dd4b15129&deviceId=d58271c7-8aa8-4ac4-aac4-632752d34002&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:44:53 +0000] "POST /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:45:12 +0000] "POST /admin/service/registration/validateDevice?serverId=af798ce4efd04996a15f744dd4b15129&deviceId=d58271c7-8aa8-4ac4-aac4-632752d34002&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0&viewOnly=true HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:45:13 +0000] "POST /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:45:33 +0000] "POST /admin/service/registration/validateDevice?serverId=af798ce4efd04996a15f744dd4b15129&deviceId=d58271c7-8aa8-4ac4-aac4-632752d34002&deviceName=Chrome&appName=Emby%20Web&appVersion=4.5.4.0 HTTP/1.1" 200 71
192.168.0.102 - - [17/Apr/2021:05:48:19 +0000] "POST /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:44 +0000] "GET /admin/service/registration/validate HTTP/1.1" 200 299
192.168.0.102 - - [17/Apr/2021:05:49:45 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:53 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:53 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:55 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:56 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:56 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:56 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:56 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:57 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:57 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:57 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:57 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:58 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:58 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:58 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:58 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:58 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:59 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:59 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:59 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:59 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:59 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:59 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:49:59 +0000] "GET /admin/service/registration/getStatus HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:50:00 +0000] "GET /favicon.ico HTTP/1.1" 200 -
192.168.0.102 - - [17/Apr/2021:05:50:05 +0000] "GET /admin/service/registration/validate HTTP/1.1" 200 299
192.168.0.102 - - [17/Apr/2021:05:50:05 +0000] "GET /admin/service/registration/validate HTTP/1.1" 200 299
192.168.0.102 - - [17/Apr/2021:05:50:05 +0000] "GET /favicon.ico HTTP/1.1" 200 -

@qwedass
Copy link

qwedass commented Jul 6, 2021

TLDR: I MANAGED TO GET EMBY PREMIERE FEATURES RUNNING ON WEB

PROBLEM:

Redirecting Emby server's mb3admin.com requests to localhost with a fake server does not unlock premiere functionality.

CAUSE:

While in the past Emby only checked the correctness of the premiere key on the server side, in recent versions it is also required for the client device to pass validation checks. Therefore, even if your Emby server passes the key check, on the machine it is running, the device you are watching on will still need to access mb3admin.com, which due to the lack of a local DNS redirect will access the real thing and fail.

This is hard to fix, as it means that you will either need to:

  • set up a local fake server on every client device & forward mb3admin.com requests to there
  • set up a DNS server that will forward mb3admin.com requests to your fake server from every device in your local network (obviously will only work from inside your network)
  • patch the client to not make (or pass) the checks

MY (HALF) SUCCESSFUL ATTEMPT

I chose to go with the most sane option - patch the client to not make (or pass) the checks.

Folder structure:

$ tree
.
├── docker-compose.yml
├── emby-crack
│   ├── cert
│   │   ├── emby-crack.crt
│   │   └── emby-crack.key
│   ├── Dockerfile
│   ├── requirements.txt
│   └── src
│       └── main.py
├── embyserver
│   ├── cert
│   │   └── emby-crack.crt
│   ├── Dockerfile
│   └── patches
│       └── connectionmanager.js

Start with generating a self-signed certificate for the fake mb3admin.com server to use:

$ openssl req -x509 -newkey rsa:2048 -days 36525 -nodes -subj '/CN=www.mb3admin.com' -addext "subjectAltName = DNS:mb3admin.com" -out emby-crack/cert/emby-crack.crt -keyout emby-crack/cert/emby-crack.key

$ cp emby-crack/cert/emby-crack.crt emby/cert/emby-crack.crt # copy the public key to the Emby server directory as we will need it

Fake mb3admin.com server (emby-crack):

The fake server is based on this repo: https://github.com/MitsuhaYuki/yuki-emby-crack

  1. requirements.txt:
asgiref==3.4.1
click==8.0.1
fastapi==0.65.2
h11==0.12.0
pydantic==1.8.2
starlette==0.14.2
typing-extensions==3.10.0.0
uvicorn==0.14.0
  1. main.py:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import uvicorn
from fastapi import FastAPI, Request

app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)

@app.get('/')
def get_main():
    return {'status': 200, 'msg': 'emby validation server successfully start'}

@app.get('/admin/service/registration/validateDevice')
@app.post('/admin/service/registration/validateDevice')
def post_validate_device():
    return {"cacheExpirationDays": 365, "message": "Device Valid", "resultCode": "GOOD"}

@app.get('/admin/service/registration/validate')
@app.post('/admin/service/registration/validate')
def post_validate():
    return {"featId": "", "registered": True, "expDate": "2099-01-01", "key": ""}

@app.get('/admin/service/registration/getStatus')
@app.post('/admin/service/registration/getStatus')
def post_get_status():
    return {"deviceStatus": "0", "planType": "Lifetime", "subscriptions": {}}

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    response = await call_next(request)
    response.headers["Access-Control-Allow-Origin"] = "*"
    response.headers["Access-Control-Allow-Headers"] = "*"
    response.headers["Access-Control-Allow-Method"] = "*"
    response.headers["Access-Control-Allow-Credentials"] = "true"
    return response

if __name__ == '__main__':
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=443,
        workers=1,
        ssl_certfile='./cert/emby-crack.crt',
        ssl_keyfile='./cert/emby-crack.key'
    )
  1. Dockerfile:
FROM python:3.8.1-alpine

COPY src/ /src/
COPY cert/ /cert/
COPY requirements.txt /requirements.txt

WORKDIR /

RUN apk add --update py-pip
RUN pip install -r requirements.txt

CMD [ "python", "./src/main.py"]

Embyserver with "patched" web player:

  1. connectionmanager.js
  • Get it from Emby:
$ curl http://<SERVER_IP>:8096/web/bower_components/emby-apiclient/connectionmanager.js >  embyserver/patches/connectionmanager.js 

or from the Emby container filesystem:

docker cp  <CONTAINER_NAME>:/system/dashboard-ui/bower_components/emby-apiclient/connectionmanager.js embyserver/patches/connectionmanager.js 
  • Modify the getRegistrationInfo method:
    The idea behind this is simple. This method does an ajax request to mb3admin.com to validate your client device for premiere status. Therefore the only thing you have to do to fool it is to replace the response from the ajax request with your own valid response. Below is a snippet of the JS file before and after the patch to better illustrate what I am talking about:

Original:

...
    var appStorage = this.appStorage,
    getRegPromise = ajax({
        url: "https://mb3admin.com/admin/service/registration/validateDevice?" + paramsToString(params),
        type: "POST",
        dataType: "json"
    }).then(function(response) {
        return appStorage.setItem(cacheKey, JSON.stringify({
            lastValidDate: Date.now(),
            deviceId: params.deviceId,
            cacheExpirationDays: response.cacheExpirationDays
        })), Promise.resolve()
    }, function(response) {
        var status = (response || {}).status;
        return console.log("getRegistrationInfo response: " + status), 403 === status ? Promise.reject("overlimit") : status && status < 500 ? Promise.reject() : function(err) {
            if (console.log("getRegistrationInfo failed: " + err), regCacheValid) return console.log("getRegistrationInfo returning cached info"), Promise.resolve();
            throw err
        }(response)
    });
...

Patched:

...
  patchedResponse = new Response(
      JSON.stringify({ "cacheExpirationDays": 365 }),
      { "status": 200 }).json();
  
  var appStorage = this.appStorage
    , getRegPromise = Promise.resolve(patchedResponse).then(function(response) {
      return appStorage.setItem(cacheKey, JSON.stringify({
          lastValidDate: Date.now(),
          deviceId: params.deviceId,
          cacheExpirationDays: response.cacheExpirationDays
      })),
      Promise.resolve()
  }, function(response) {
      var status = (response || {}).status;
      return console.log("getRegistrationInfo response: " + status),
      403 === status ? Promise.reject("overlimit") : status && status < 500 ? Promise.reject() : function(err) {
          if (console.log("getRegistrationInfo failed: " + err),
          regCacheValid)
              return console.log("getRegistrationInfo returning cached info"),
              Promise.resolve();
          throw err
      }(response)
  });
...
  1. Dockerfile:
FROM emby/embyserver:4.6.4.0

# Copy the patched file
COPY patches/connectionmanager.js /system/dashboard-ui/bower_components/emby-apiclient/connectionmanager.js

# Copy the fake mb3admin.com server cert
COPY cert/emby-crack.crt /emby-crack.crt

# Install the fake mb3admin.com server cert
RUN cat /emby-crack.crt >> /etc/ssl/certs/ca-certificates.crt && \
    rm /emby-crack.crt

Create the docker-compose.yml:

version: "2.3"
services:
  ##############################################################################
  # emby-crack
  ##############################################################################
  emby-crack:
    build: ./emby-crack
    hostname: mb3admin.com
    container_name: emby-crack
    networks: 
      - net
  ##############################################################################
  # embyserver
  ##############################################################################
  embyserver:
    build: embyserver
    container_name: embyserver
    hostname: embyserver.local
    # runtime: nvidia # Expose NVIDIA GPUs
    networks: 
      - net
    environment:
      - UID=1000 # The UID to run emby as (default: 2)
      - GID=100 # The GID to run emby as (default 2)
      - GIDLIST=100 # A comma-separated list of additional GIDs to run emby as (default: 2)
    volumes:
      - /path/to/programdata:/config # Configuration directory
      - /path/to/tvshows:/mnt/share1 # Media directory
      - /path/to/movies:/mnt/share2 # Media directory
    ports:
      - 8096:8096 # HTTP port
      - 8920:8920 # HTTPS port
    devices:
      - /dev/dri:/dev/dri # VAAPI/NVDEC/NVENC render nodes
      - /dev/vchiq:/dev/vchiq # MMAL/OMX on Raspberry Pi
    restart: unless-stopped
    depends_on:
      - emby-crack

networks:
  ##############################################################################
  # shared network
  ############################################################################## 
  net:
    driver: bridge

Run the docker-compose.yml:

$ docker-compose up # (reset with docker-compose down --remove-orphans)

Initialize server:

Go through to the inital server setup, then go to Server Settings --> Emby Premiere and input whatever key you like

Okay, but if the server is "patched", why is it only "half" successful?

I'm glad you asked. As you probably saw, the key you entered was accepted, yet the premiere features are not working still. This is due to the problem described in CAUSE.

But hold on, we patched that JS file on the client side, didn't we?
Yes, we did. That can be confirmed by getting the file from the running embyserver instance:

$ docker cp embyserver:/system/dashboard-ui/bower_components/emby-apiclient/connectionmanager.js patchedmngr.js

To prove it is not the same as the one returned by Emby we can do:

$ curl http://<SERVER_IP>:8096/web/bower_components/emby-apiclient/connectionmanager.js > returnedmngr.js
$ diff patchedmngr.js returnedmngr.js  # outputs the difference in the files

Somehow Emby manages to cache the original JS file and return that instead of the one we replaced it with.
I have tried to:

  1. Stop my internet connection (running the docker-compose on localhost)
  2. Create the services (docker-compose up -d)
  3. Delete the JS file from the container filesystem (docker exec embyserver rm /system/dashboard-ui/bower_components/emby-apiclient/connectionmanager.js)
  4. Restart the services (docker-compose stop && docker-compose up)

And yet when I run:

$ curl http://localhost:8096/web/bower_components/emby-apiclient/connectionmanager.js > returnedmngr.js

I still get the original file.

Okay, but then how did you make it work at all?

By overriding the JS file in the browser with the help of Chrome's developer tools.

Replacing the JS file manually with the patched file we created earlier and then refreshing the Emby player tab unlocks premiere functionality.

CLOSING THOUGHTS:

Faking the mb3admin.com on the server end is fairly trivial at this point. However, faking it on the client side has proved to be more difficult.

The good news are that there are hints in the JS files of the Emby server, which suggest that they are used for all clients, including Android and IOS clients (I assume they are built using electron). This means that if the connectionmanager.js is successfully patched on the server side there is a possibility of the patch working across all clients without further modification required.

The bad news are that currently I have not found a way to make Emby return the patched connectionmanager.js. Looking closer at the old open sources I can see that it is using .NET Web Dashboard to serve http/s requests. However, this article does not help me with finding a way to reset its cache, as it requires access to the source code. The way to progress further is to decompile the /system/Emby.Web.dll DLL and see what can be done for the cache reloading.

@demogorgonz
Copy link

demogorgonz commented Jul 21, 2021

@qwedass i have patched the Emby.Web.dll to point to another FQDN owned by me, and it works from browsers (PC, phone, TV), however it is hardcoded in every standalone app meaning:

  • Samsung TV emby app, have it's own variant of Emby.Web.dll
  • Android & iOS apps have https://mb3admin.com hardcoded too.

At least, i got new domain name for premier verification and don't have to change hosts file or accept self signed certs (from web client/browser).

To make this work we would have to patch all standalone apps to point to another domain since we can't issue valid cert for mb3admin.com

@qwedass
Copy link

qwedass commented Jul 21, 2021

@qwedass i have patched the Emby.Web.dll to point to another FQDN owned by me, and it works from browsers (PC, phone, TV), however it is hardcoded in every standalone app meaning:

  • Samsung TV emby app, have it's own variant of Emby.Web.dll
  • Android & iOS apps have https://mb3admin.com hardcoded too.

At least, i got new domain name for premier verification and don't have to change hosts file or accept self signed certs (from web client/browser).

To make this work we would have to patch all standalone apps to point to another domain since we can't issue valid cert for mb3admin.com

@demogorgonz
If I understood correctly, you have:
Patched the server Emby.Web.dll to force the server to authenticate against a different FQDN, to avoid having to set up a redirect within /etc/hosts (which is what I effectively do within my docker compose network).

I did not get how you managed to make the server return the patched connectionmanager.js? In my testing just patching the server was not enough, as the web client also tries to authenticate against mb3admin.com (which is why I patched the connectionmanager.js with a hard-coded valid authentication response).

Regarding the standalone clients, at first I assumed as well that they will have their own authentication logic (instead of having to pull js files off of the server). But then I saw one of the server js files referring to "native" versions of the js files for iOS and Android clients. This made me believe that the "native" clients are nothing more than a browser without an address bar, but I did not investigate much further.

How do you know that the standalone apps do not download the authentication logic from the server? Did you try to MITM the connection between them to see what is being exchanged?

@demogorgonz
Copy link

demogorgonz commented Jul 21, 2021

@qwedass You understood correctly, Emby.Web.dll provide web client with auth URL https://mb3admin.com, replacing that url in the dll file provide client with another URL for which i can generate valid SSL to avoid importing cert to all clients to be able to stream TV channels. On first look, server seemed like control point to provide clients with auth URL.

I will have to look further into this, will checkout android app since it can be easily decompiled to be sure what logic standalone clients use to obtain mb3admin.com url. Hopefully it is all server side logic so that we can provide patch set of files to unlock all features.

Will post if i find anything useful :).

Capture

@demogorgonz
Copy link

@qwedass just to confirm, tried iOS and Samsung TV, both clients have hardcoded https://mb3admin.com, if you are using local dns with the record as per @all3kcis instructions, you might be able to access premier features only if the client can handle(trust) self signed cert. Since that is not case for my TV i am locked on features 😞 . Unfortunately the URL is not provided from server side.

@morths
Copy link

morths commented Jul 22, 2021

@qwedass just to confirm, tried iOS and Samsung TV, both clients have hardcoded https://mb3admin.com, if you are using local dns with the record as per @all3kcis instructions, you might be able to access premier features only if the client can handle(trust) self signed cert. Since that is not case for my TV i am locked on features 😞 . Unfortunately the URL is not provided from server side.

Same thing on my 2 LG TVs (webos). As posted a few months back, i managed (after too many failed attempts!) to redirect both TVs to my fake local https://mb3admin.com, only to learn that this wasn't enough... Unfortunately webos TVs do not allow self signed certs (trust me i've searched it), which is absolutely critical for emby app to succesfully contact the local fake https://mb3admin.com and unlock premier features on TV side! So, since this is not possible, the emby app on my TVs remain forever premierless..

@demogorgonz
Copy link

demogorgonz commented Jul 30, 2021

Have anyone tried sslstrip with arpspoof to strip ssl from mb3admin.com ?

@sasagr
Copy link

sasagr commented Aug 10, 2021

managed to get premiere on the raspberry pi where my emby server run, using the docker but on window 10 when I go to emby.media or 192.168.x.xxx:8096... I dont get the premiere. I edited the host file in windows to have the mb3admin.com but it seems not to be enough. Any idea what to do?

@adequate-coder
Copy link

adequate-coder commented Nov 4, 2021

The way to progress further is to decompile the /system/Emby.Web.dll DLL and see what can be done for the cache reloading.

You can disassemble this file with ildasm and then reassemble it with ilasm after making the necessary changes.

E.g. if you have Emby server installed in /system on a Linux install.

mkdir -p /tmp/patches
cd /tmp/patches
ildasm /system/Emby.Web.dll -out=Emby.Web.il

Now all the JS files are in the current directory (/tmp/patches) and you can apply the registration patches with a regular text editor.

vim /tmp/patches/Emby.Web.dashboard_ui.bower_components.emby_apiclient.connectionmanager.js

I personally like to replace the getRegPromise with a fully mocked server response.

-getRegPromise=ajax({url:"https://mb3admin.com/admin/service/registration/validateDevice?"+paramsToString(params),type:"POST",dataType:"json"})
+getRegPromise=Promise.resolve(new Response('{"cacheExpirationDays":365,"message":"Device Valid","resultCode":"GOOD"}').json())

Reassemble the DLL and overwrite the original assembly.

ilasm -dll /tmp/patches/Emby.Web.il -out=/system/Emby.Web.dll

On Windows you need to replace -out= with /out=.

The binaries for a typical 64-bit Linux install can be downloaded here.

Or use the native nuget client if you are on Windows.

nuget install Microsoft.NETCore.ILDAsm
nuget install Microsoft.NETCore.ILAsm

For good measure, also patch the duplicate code in /system/dashboard-ui/bower_components/emby-apiclient/connectionmanager.js because I'm unsure when this file is served instead of the file embedded in Emby.Web.dll.


After all this is said and done, some clients are unlocked while other still require client-side patching. There is a discrepancy between platforms where some platforms load the connection manager from the server while other apps are self-contained (e.g. the Electron edition of Emby Theater).

So far, Emby for Android seems to be fully unlocked after patching the connection manager in both locations. Emby Theater for Smart TV still shows the Premiere nagging popup so I suspect it contains its own connection manager. I don't know where to go from here without rooting my TV / other devices with self-contained Emby Theaters.

@1ucay
Copy link

1ucay commented Dec 16, 2021

I tried replace mb3admin.com with dnSpy in dll files, but it wasnt perfect. Too much work. Some plugins in repositary have hardcoded mb3admin.com in licence class. So on my vps I configure apache with alias mb3admin.com. In Windows I add line in C:/windows/system32/driver/etc
myiptovps mb3admin.com

I generated certificate for domains (Im using Virtualmin on vps) and set in conf of apache webserver.
mb3admin.com
www.mb3admin.com

Then I installed this cert.cer on Windows.

index.php

<?php
if ( true ){
	$file = 'data.log';
	$content = file_get_contents($file);
	$content .= date('Y-m-d H:i:s').' : '.$_SERVER['REMOTE_ADDR'].' URL : '.$_SERVER['REDIRECT_URL'].' Query : '.json_encode($_REQUEST)."\r\n";
	file_put_contents($file, $content);
}

if ( $_SERVER['REDIRECT_URL'] == '/admin/service/registration/validate' ){

	header('content-type:application/json');
	$date = new DateTime();
	$date->add(new DateInterval('P14D'));
	echo '{"featId":"'.$_POST['feature'].'","registered":true,"expDate":"'.$date->format('Y-m-d').'","key":"'.$_POST['key'].'"}';

} elseif ($_SERVER['REDIRECT_URL'] == '/admin/service/services/ppipn.php' ){

	header('content-type:application/json');
	echo '{}';	

} elseif ($_SERVER['REDIRECT_URL'] == '/admin/service/appstore/register' ){

	header('content-type:application/json');
	echo '{"featId": "","registered": true,"expDate": "2099-01-01","key": ""}';

} elseif ($_SERVER['REDIRECT_URL'] == '/admin/service/registration/getStatus' ){

	header('content-type:application/json');
	echo '{"deviceStatus": "0", "planType": "Lifetime", "subscriptions": {}}';

} elseif ($_SERVER['REDIRECT_URL'] == '/emby/Plugins/SecurityInfo' ){

	header('content-type:application/json');
	echo '{SupporterKey: "", IsMBSupporter: true}';

} elseif ($_SERVER['REDIRECT_URL'] == '/admin/service/registration/validateDevice' ){

	header('content-type:application/json');
	echo '{"cacheExpirationDays":365,"resultCode":"GOOD","message":"Device Valid"}';

}

?>

@maikeln80
Copy link

Hi, when restart apache show error,
SSLCertificateFile takes one argument, SSL Server Certificate file ('/path/to/file' - PEM or DER encoded)

cert create con /etc/apache2

on mb3admin.conf
SSLEngine on
SSLCertificateFile "/etc/apache2/mb3admin.crt" # Self signed certificate
SSLCertificateKeyFile "/etc/apache2/mb3admin.key"

with "" or without "" same error

Any idea how to resolve? Thanks

@potatoru
Copy link

potatoru commented Jan 20, 2022

To be honest, there is no easy way to make Premiere work everywhere. It works for me, but my way may not suit everyone.

Emby server

I use the method described above (webserver with PHP script). I also have a DNS server running on my homelab (Adguard Home) so I pointed mb3admin.com to my local webserver. Nothing more to add to the instructions above.
Don't forget to edit /opt/emby-server/etc/ssl/certs/ca-certificates.crt and add your self-signed certificate at the end.

Web client

I often use the web client outside my home, so more practical way is to patch the web UI (as described above) and replace mb3admin.com with your public domain pointing to the same web server (mb3.example.com for me) so it will work everywhere. You also need a valid SSL certificate for your custom domain (let's encrypt or just cloudflare will work).

For example (Debian 11 server):

ildasm /opt/emby-server/system/Emby.Web.dll -out=Emby.Web.dll
sed -i 's/mb3admin.com/mb3.example.com/g' Emby.Web.dashboard_ui.modules.emby_apiclient.connectionmanager.js
ilasm -dll Emby.Web.dll -out=/opt/emby-server/system/Emby.Web.dll

iOS app / Android app

Inside my home network it works with the fake web server and DNS. Outside my home I usually connected to my home network using WireGuard so my DNS and web server are always accessible.

Android TV

As ATV app doesn't accept self-signed certificates and there is no way to install certs to the device (correct me?) I patched the emby app.
You need Android SDK and apktool installed on your computer. Next you disassemble, patch, build and sign the app.

For example (now I use my Windows PC):

  1. Decompile the app:
    .\apktool.bat d .\app-google-release.apk

  2. Replace all mb3admin.com entries with your mb3.example.com domain in files:

smali_classes2\mediabrowser\apiinteraction\connect\ConnectService.smali
smali_classes2\tv\emby\embyatv\util\LogReporter.smali
smali_classes2\tv\emby\embyatv\validation\AppValidator.smali
  1. Build the app:
    .\apktool.bat build --output patched.apk .\app-google-release\

  2. Create an aligned APK file:
    zipalign 4 .\patched.apk .\patched-sign.apk

  3. Create a sign key:
    keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

  4. Sign the APK:
    apksigner.bat sign --ks .\my-keystore.keystore .\patched-sign.apk

  5. Install signed APK to your device.

I'm not sure if you can patch an Apple TV or WebOS app. You also need to patch apps after every update and maintain PHP script with the API changes. IMO, it's not worth to waste time setting up and maintaining a web server, decompiling apps, etc. It was really fun to play with tools and things, but I got the Premiere. It really worth it.

@lvitti
Copy link

lvitti commented Mar 8, 2022

mkdir -p /tmp/patches
cd /tmp/patches
ildasm /system/Emby.Web.dll -out=Emby.Web.il

Now all the JS files are in the current directory (/tmp/patches) and you can apply the registration patches with a regular text editor.

vim /tmp/patches/Emby.Web.dashboard_ui.bower_components.emby_apiclient.connectionmanager.js

I personally like to replace the getRegPromise with a fully mocked server response.

-getRegPromise=ajax({url:"https://mb3admin.com/admin/service/registration/validateDevice?"+paramsToString(params),type:"POST",dataType:"json"})
+getRegPromise=Promise.resolve(new Response('{"cacheExpirationDays":365,"message":"Device Valid","resultCode":"GOOD"}').json())

Reassemble the DLL and overwrite the original assembly.

ilasm -dll /tmp/patches/Emby.Web.il -out=/system/Emby.Web.dll

@adequate-coder

I got this error after trying the ilasm options:

	*** Error Report ***
	Version: 4.6.4.0
	Command line: C:\EmbyServer\system\EmbyServer.dll
	Operating system: Microsoft Windows 10.0.19044
	Framework: .NET Core 3.1.13
	OS/Process: x64/x64
	Runtime: C:/EmbyServer/system/System.Private.CoreLib.dll
	Processor count: 8
	Data path: C:\EmbyServer\programdata
	Application path: C:\EmbyServer\system
	System.BadImageFormatException: System.BadImageFormatException: Could not load file or assembly 'Emby.Web, Version=4.6.4.0, Culture=neutral, PublicKeyToken=null'. An attempt was made to load a program with an incorrect format.
	File name: 'Emby.Web, Version=4.6.4.0, Culture=neutral, PublicKeyToken=null'
	   at Emby.Server.Implementations.ApplicationHost.GetComposablePartAssemblies()
	   at Emby.Server.Implementations.ApplicationHost.DiscoverTypes()
	   at Emby.Server.Implementations.ApplicationHost.Init()
	   at EmbyServer.HostedService.StartAsync(CancellationToken cancellationToken)
	
	
	Source: Emby.Server.Implementations
	TargetSite: System.Collections.Generic.List`1[System.Tuple`2[System.Reflection.Assembly,System.String]] GetComposablePartAssemblies()```

@DrissiReda
Copy link

I have tried everything here and nothing works.

I am using version 4.7.0.32

I can no longer find the file /system/dashboard-ui/bower_components/emby-apiclient/connectionmanager.js.

But I can still find the file /system/Emby.Web.dll .

The variable getRegPromise changed, it is now feature=ajax({...

Still, nothing worked it thanks me for validating the key, but no features work.

@potatoru
Copy link

potatoru commented Apr 4, 2022

@DrissiReda "unpack" Emby.Web.dll with ildasm and you will find the connectionmanager.js

See examples above.

@DrissiReda
Copy link

@DrissiReda "unpack" Emby.Web.dll with ildasm and you will find the connectionmanager.js

See examples above.

Like I said, I have found it, after unpacking that dll, and I modified it, the UI shows I have emby premiere but I have no features. And Emby Theather doesn't work.

@rossi29
Copy link

rossi29 commented May 31, 2022

Good morning all,
can a charitable person explain to me how to do it
thank you

@DrissiReda
Copy link

Good morning all, can a charitable person explain to me how to do it thank you

Don't bother, it does not work anymore.

@rossi29
Copy link

rossi29 commented Jun 1, 2022

ok thank you for your answer

@ogry
Copy link

ogry commented Jun 1, 2022 via email

@rossi29
Copy link

rossi29 commented Jun 3, 2022

Seulement cinq mots : SUIVEZ ATTENTIVEMENT TOUTES LES INSTRUCTIONS En miércoles, 1 de junio de 2022, 18:57:46 CEST, rossi29 @.> escribió : @rossi29 a commenté cet essentiel. ok merci pour votre réponse — Répondez directement à cet e-mail, consultez-le sur GitHub ou désabonnez-vous. Vous recevez ceci parce que vous avez commenté. ID du message : @.>

Sorry, I don't understand your message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment