Injecting JS into electron apps (And adding RTL support for ‘Microsoft Teams’)
Heya !
Last week our team at Microsoft had switched our intra-organization communication app — we moved from Slack to Teams, Microsoft’s own built service.
Both services offer similar capabilities, each with its own strengths and weaknesses; But one thing they both lack is RTL (Right-To-Left) support.
Our group chats often mix both English and Hebrew, and having your sentences mis-aligned proves to be quite a pain, ‘coercing’ us to use too many line breaks and leading to unnecessary confusion.


Creating an RTL solution for Microsoft Teams
I wanted to scratch my own itch, and this had led me to search for an easy way to add RTL support for the app, at least a temporary one, before it is supported natively.
I started with peeking over at Teams directory; I could quickly see that much like Slack, it’s built on top of Electron — A framework that lets one create cross-platform desktop apps with web front-end technologies.


Above, you can see a typical electron app directory tree:
- app.asar.unpacked — Usually contains static assets and 3rd party npm modules
- assets — Images, Icons and similar static files
- locales — Multi language translations
- app.asar — The packed app, in Electron’s archive format
- electron.asar — Electron supplied libraries
Easy — just find a .js file that is loaded by Teams in the app.asar.unpacked, and add a custom payload there !
Or so I thought, at least. Going over the app’s unpacked directory unveiled only pre-compiled node modules (Basically, a bunch of .dll’s).


The next thing that came to mind was creating a proxy dll that would inject a given payload; This was quickly dismissed as it would require a separate solution for my Mac-using colleagues.
With DLL proxying off the table, I thought of two other methods that may work.
- Repackaging the app with a custom script that would provide the RTL logic


2. Try to load the app in debug mode, and use chrome’s devtools protocol to inject a script in runtime (using Runtime.evaluate)
The clear advantage of the latter is that it won’t be overwritten by future updates, which tend to be quite frequent; And so I’ve decided to go through the second route.
Running Teams with a debugger server
Electron uses chromium as its under the hood engine, and so it’s possible to pass chromium-specific arguments when running electron based apps.
One of many useful parameters you may pass is ‘remote-debugging-port’, which let’s you connect a remote debugger to every window the app creates.
Firing up Teams with the flag set to a port of my choice, I could easily test it by surfing to chrome://inspect in chrome, after enabling network device lookup.


Great — feels like progress ! Next, I went on to inspect the chat window to get a sense of the elements I’d need to operate on.


I’ve identified three element sets that benefit from RTL-ing, and those are:
- Chat messages — CSS Selector `.message-body-content`
- Chat previews (To the left) — CSS Selector`.ts-channel-list-entry-preview`
- Chat text input — CSS Selector `.ts-edit-box .cke_editable`
As for the RTL logic, I could just plug in a dir=”auto” attribute (Supported by most major browsers) to these elements and the browser should render them automatically.


And now, for the injection itself.
From Chrome DevTools Protocol: […] your application can discover available pages by requesting: http://localhost:port/json and getting a JSON object with information about inspectable pages along with the WebSocket addresses that you could use in order to start instrumenting them.
That sounds promising — let’s take a look.


The endpoint outputs the active windows of the debugged process, and switching between panes in Teams we could see the various websocket debug urls for each pane. For now, it looks like I’m most interested in the ‘Chat’ window.
Going through the DevTools protocol rich API, we would notice that we can evaluate a javascript payload using the ‘Runtime.evaluate’ method.


Communicating with the app is pretty straightforward from hereon, we connect to the websocket, and can send commands based on the following JSON:
{“id”: someId, “method”: methodName, “params”: {…}}
The quickest way to test this was via trying to evaluate an alert:


Now all we need to do is:
- Write the RTL-ing payload
- Open Teams w/ remote debugging enabled
- Find the chat window
- Inject script into said window
- Profit
The payload, together with a python-based Teams runner (It’s in charge of running Teams debugged and injecting the script) is available here, it supports both Windows and Mac.
I’ve also packaged it into a chrome extension that uses the same payload as a content script.
A Quick Recap
- We talked about a few potential script injection mechanism to electron based applications
- We investigated the DevTools Protocol and have seen how to use it to evaluate a js payload
- We have added a decent RTL support for Microsoft Teams
Resources & Thanks to:
- Chrome DevTools Protocol Docs
- Electron-Inject Python Library
- You, hope you had a great read !
Feel free to leave a comment below :)