Skip to content

Instantly share code, notes, and snippets.

@kopiro
Forked from Informatic/README.md
Created May 6, 2022 18:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kopiro/c9a3fa6767fd3181c590e32b04b9014e to your computer and use it in GitHub Desktop.
Save kopiro/c9a3fa6767fd3181c590e32b04b9014e to your computer and use it in GitHub Desktop.
openlgtv webOS hacking notes

This is just a dump of some interesting undocumented features of webOS (3.8 specifically, on early 2018 4k LG TV) and other development-related tips.

Homebrew app ideas

Receiving DIAL/SSDP

Any installed application can receive DIAL start requests by adding "dialAppName": "NAME" in its appInfo.json. YouTube and Netflix apps have some special handling, but they still can be overriden by an unofficial app.

IMPORTANT: official YouTube app has "dialAppName": "youtube". Installed apps are (or rather seem to be) ordered lexicographically based on their IDs - when multiple apps declare the same dialAppName the last one will be the one triggered via DIAL. (com.youtube.adfree < youtube.leanback.v4 < youtube.leanforward)

TV Input embedding

Web apps can embed connected inputs surface in their DOM:

<video autoplay style="width:50%;height:50%">
  <source type="service/webos-external" src="ext://hdmi:1"></source>
</video>

Supported sources: ext://hdmi:1, ext://hdmi:2, ext://hdmi:3, ext://hdmi:4, ext://comp:1, ext://av:1, ext://av:2. Additionally TV stream can be embedded with src="tv://" and type="service/webos-broadcast"

Seems like only a single external input can be displayed at the same time. (at least on my TV, equivalent to multiview limitations)

Registering an app as an input (can be used to autostart an app)

Following needs to be added to appinfo.json:

  • "supportGIP": true
  • largeIcon needs to be set (otherwise eim just crashes)

When starting up an app following call needs to be executed to register it:

webOS.service.request("luna://com.webos.service.eim", {
  method: "addDevice",
  parameters: {
    "appId": "org.webosbrew.hbchannel", // your application ID, required
    "pigImage": "", // required, preview image rendered in "All inputs", relative to main application directory, can be just an empty string
    "mvpdIcon": "", // required on webOS <3.x
    "type": "MVPD_IP", // optional, no idea (can be MVPD_IP or MVPD_RF)
    "showPopup": true, // optional, shows a toast with info that default input has been changed to label
    "label": "application name", // optional, used in toast message only
    "description": "testing", // optional, description rendered in "All inputs"
  },
  onSuccess: function (res) { console.info('success:',res); },
  onFailure: function (res) { console.info('failure:', res); }
 });

luna://com.webos.service.eim/deleteDevice {"appId":"..."} call can be used to unregister an app.

This also works over luna-send-pub for any app, as long as its appinfo.json is properly set up, in case anyone wants to debug this manually. (source app ID is not verified)

Userscripts in apps

Userscript present in webOSUserScripts/userScript.js will be loaded as a userscript in app webviews / frames. (including frames in origins outside of app root)

Example app: https://github.com/FriedChickenButt/youtube-webos

WebAppMgr User-Agent change

Add the following to appinfo.json:

{
  "vendorExtension": {
    "userAgent":"$browserName$/$browserVersion$ ($platformName$-$platformVersion$), _TV_$chipSetName$/$firmwareVersion$ (LG, $modelName$, $networkMode$)"
  },
  "trustLevel": "netcast"
}

Note: No idea what trustLevel actually means - it seems to somewhat change supported web APIs and makes window.PalmServiceBridge unavailable. (launch params are in window.launchParams)

WebAppMgr - Disabling CORS enforcement

In order to disable CORS enforcement use:

{
  "vendorExtension": {
    "allowCrossDomain": true
  },
  "trustLevel": "netcast"
}

Invisible apps

Apps with "visible": false can be launched and have their services accessed, but they don't show up on main screen.

Note: LG Content Store is periodically queried for software updates of all installed applications (including homebrew/developer and system apps, app ids and versions are transmitted)

Multiview app

At least on my TV multiview doesn't seem to be officially supported anywhere in the UI. Yet still - it can be launched via telnet when rooted: /usr/bin/com.webos.app.multiview. This can possibly be wrapped in a proper app.

State monitoring

# Suspend / resume
luna-send -i 'luna://com.webos.service.tvpower/power/getPowerState' '{"subscribe":true}'

# Currently running application
luna-send -i 'luna://com.webos.applicationManager/getForegroundAppInfo' '{"subscribe":true}'

Injecting mitmproxy CA

Note: see Filesystem overlays below, /usr/share/ca-certificates/sdp + /etc/ssl/certs

mkdir -p /tmp/overlayfs/upper /tmp/overlayfs/work && mount -t overlay -o lowerdir=/etc/ssl/,upperdir=/tmp/overlayfs/upper/,workdir=/tmp/overlayfs/work/ overlay-ssl /etc/ssl/
cd /etc/ssl/certs
curl http://mitm.it/cert/pem > mitmproxy.crt
ln -s mitmproxy.crt $(openssl x509 -in /etc/ssl/certs/mitmproxy.crt -subject_hash_old -noout).0
cat mitmproxy.crt >> trusted_cas.crt
cat mitmproxy.crt >> ca-certificates.crt

# Also TODO: /usr/share/ca-certificates/sdp

Extra note: TVs have their own (Multiple) Client Keys (and certificates), that are used for mutual TLS authentication by some remote services. They are stored securely, and there's no way to Read them.

Disable "Wedge" / "My Content" / promotion bar on home screen

luna-send -n 1 -f luna://com.webos.service.config/setConfigs '{"configs":{"com.webos.service.favoriteservice.enableWedge": false}}'`

(note: this only persists, if *.lgtvsdp.com traffic is blocked)

Increase pmlogd logging

By default pmlogdaemon logs only topics listed in /etc/PmLogDaemon/whitelist.txt. To enable all logging use:

luna-send -n 1 -f 'luna://com.webos.pmlogd/setdevlogstatus' '{"recordDevLogs":false}'
luna-send -n 1 -f 'luna://com.webos.pmlogd/getdevlogstatus' '{}'

# After enabling all devlogs specific contexts can be adjusted using PmLogCtl
PmLogCtl set '*' 'debug'

# Newer webOS:
luna-send -n 1 -f luna://com.webos.service.config/setConfigs '{"configs": {"system.collectDevLogs": true}}'
luna-send -n 1 -f luna://com.webos.service.config/getConfigs '{"configNames":["system.collectDevLogs"]}'

(note: you most probably want to disable rdxd first, as per rootmytv immutable mounts...) (also: i got a tip this doesn't work anymore on newer webos/pmlogd versions)

Luna Service Bus debugging

# Log traffic
ls-monitor

# List connected clients
ls-monitor -l

# Inspect supported requests
ls-monitor -i com.webos.service...

# Some requests schemas are present in /etc/palm/schemas

Magic Remote IR blaster

luna-send -a 'important-dunno-why-lol' -n 1 -f 'luna://com.webos.service.mrcu/ir/pushIrCodes' '{ "irCodes": "0xaa 0x00 0x00 0x00 0x0c 0x94 0x00 0x00 0x58 0x0f 0x02 0x00 0xa2 0x00 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x04 0x1a 0x07 0x07 0x81 0x2d 0x07 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 0x07 0x81 0x2d 0x07 0x07 0x81 0x13 0x03 ", "irCodeCount": 174 }'

todo: research what's the actual encoding scheme

When enabling magic remote IR control of appliances LG's services are accessed to download new keycodes. These are then stored in /mnt/lg/user/irdbmanager (oss_audio_append_db.txt and setting/oss_setting_info_audio.txt). Some default codes are also present in /usr/share/irdbmanager.

Nice potentially hackable apps

  • My starter (triggered by pressing clock on home screen) → /usr/palm/applications/com.webos.app.mystarter/qml/main.qml
  • Screensaver (fireworks) → /usr/palm/applications/com.webos.app.screensaver/qml/main.qml
  • Home screen layout → seems like all .qml files are bundled in surface-manager-starfish (managed to extract it without filenames with binwalk - someone needs to make some qt resources unpacker) - but it also accepts an env variable with custom .qml startup file.

Custom Magic Remote cursors

Cursors are rendered by surface-manager-starfish (surface-manager upstart service), and are loaded from /usr/share/im. cursorTypeAszMstN.png is the default "idle" medium-sized cursor. S/M/L in that filename is cursor size. /usr/share/im needs to be mounted/overlaid early on in the boot process (needs testing), or surface-manager needs to be manually restarted after applying the patch. Stays on between suspends when Quick Start+ is enabled. Filesystem overlay like below can be used.

Filesystem overlays

Use https://gist.github.com/Informatic/db387d512bf4ae5512d9f644c4d219d2

Download and install an ipk in (root) shell

curl -L https://github.com/mariotaku/moonlight-tv/releases/download/v0.6.0/com.limelight.webos_0.6.0_arm.ipk -o /media/internal/downloads/app.ipk && \
luna-send -i -f luna://com.webos.appInstallService/dev/install '{"id":"com.limelight.webos","ipkUrl":"/media/internal/downloads/app.ipk","subscribe":true}'

App launch via shell

luna-send -n 1 -f luna://com.webos.service.applicationManager/launch '{"id":"com.webos.app.screensaver"}' 

Screenshot

luna-send -n 1 -f 'luna://com.webos.service.tv.capture/executeOneShot' '{"path":"/tmp/capture.png","method":"DISPLAY","format":"PNG"}'
# Supported formats: BMP, JPG, PNG, RGB, RGBA, YUV422
# Supported methods: SCREEN/DISPLAY (alias?), SCREEN_WITH_SOURCE_VIDEO, VIDEO, GRAPHIC, SOURCE/SCALER (alias?)

# Note: com.webos.service.tv.capture above has been renamed to com.webos.service.capture on some devices, call signature is the same.

(Note: this is also available over SSAP as: ssap://tv/executeOneShot - returns imageUri attribute with HTTP link)

Factory mode / instart / instop...

Don't fuck with this. You WILL most probably brick your TV.

# -> binwalk -e /usr/bin/surface-manager-starfish && grep -Ri factory *.extracted
luna-send -n 1 -f luna://com.webos.service.applicationManager/launch '{"id":"com.webos.app.factorywin","params":{"id":"executeFactory","irKey":"inStart"}}'
# Alternative irKeys: powerOnly, inStart, ezAdjust, inStop, pCheck, sCheck, tilt

# ezAdjust is service menu 1
# inStart is service menu 2
# inStop is factory reset (?)
# pCheck will override some picture settings for lcd testing
# sCheck will boost sound to test audio
# tilt opens white screen that does nothing and locks remote buttons, goes away after a reboot

# Also, rather obviously, this can be triggered over SSAP as well... :)

# -> /usr/palm/applications/com.webos.app.factorywin
# Password: 0413
# Bang & Olufsen password: 1925
# "USB Log" (?) password: 1126

"Host Diagnostics" view

Go to settings → Programmes → Hover "Programme Tuning & Settings" and press "1" button five times.

Disable 15 minute no-signal auto power off

luna-send -n 1 luna://com.webos.settingsservice/setSystemSettings '{"settings":{"autoOff15Min":"off"},"category":"time"}'

Accessing devmode SSH without full webOS SDK

tv=10.11.12.13
# 1. Enable devmode
# 2. Enable keyserver
# 3. Download key and decrypt using Passphrase displayed on a screen
openssl rsa -in <(curl http://$tv:9991/webos_rsa) > webos_rsa && chmod 600 webos_rsa
# 4. just ssh
ssh prisoner@$tv -i webos_rsa -p 9922 bash -i

Tip: @webosose/ares-cli NPM package is sufficient to play with webOS TV development. (ares-package, ares-install, ares-launch...) A quick tip on how to configure everything is available here: FriedChickenButt/youtube-webos#13 (comment)

Using sshd after deploying RootMyTV

Note: dropbear sshd is now bundled by default with Homebrew Channel app. If you've rooted Your TV using https://rootmy.tv you may already have it, you just need to enable it in Homebrew Channel Settings view.

Run this manually in telnet session:

mkdir -p /var/run/sshd && /media/cryptofs/apps/usr/palm/services/com.palmdts.devmode.service/binaries-armv71/opt/openssh/sbin/sshd -o StrictModes=no -f /media/cryptofs/apps/usr/palm/services/com.palmdts.devmode.service/binaries-armv71/opt/openssh/etc/ssh/sshd_config -h /var/lib/devmode/sshd/ssh_host_rsa_key -o PasswordAuthentication=yes -o PermitRootLogin=yes -o PermitUserEnvironment=yes -o AuthorizedKeysFile=/media/developer/.ssh/authorized_keys -D -p 9922

Afterwards, SDK needs to be reconfigured to use root username:

ares-setup-device -m DEVICE -i "username=root"

SSH public key can be added to: /media/developer/.ssh/authorized_keys

Inspector for development apps is present on: http://TV_IP:9998

Running native app in jailer

jailer -d -t native_devmode -p /media/developer/apps/usr/palm/applications/com.limelight.webos/ -i com.limelight.webos $(which id)

Controlling screen backlight

luna-send -n 1 "luna://com.webos.service.tvpower/power/turnOnScreen" '{}'
luna-send -n 1 "luna://com.webos.service.tvpower/power/turnOffScreen" '{}'
luna-send -n 1 'luna://com.webos.settingsservice/setSystemSettings' '{"category":"picture","settings":{"energySaving":"screen_off"}}'

luna-send-pub -f -n 1 luna://com.webos.service.applicationManager/launch '{"id":"com.webos.app.tvhotkey","params":{"activateType":"energy-saving-mode"}}'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment