Binary JavaScript Embedment (CS4/CS5)
April 13, 2010 | Tips | en
The jsxbin format (“Binary JavaScript”) is a boon for developers because it enables them to distribute a script without exposing its precious code. The ExtendScript ToolKit (ESTK) offers an Export to Binary feature which instantly converts a human-readable js(x) file into jsxbin. InDesign can then interpret this file as if it were a pure JavaScript. The only (boring!) problem is that the encoded script is not allowed to load a persistent session through the #targetengine
directive. Here is a secret trick to bypass this limitation with a single encrypted file.
Part 1 — the Old School Launcher
The usual way to work around the no-targetengine-in-jsxbin rule is to use a separate ‘launcher’ written in human-readable JavaScript. The JS launcher creates the persistent engine (#targetengine 'myScriptSession'
), then it loads and runs the encrypted jsxbin file through app.doScript(File(jsxBinFilePath))
. Beforehand, you must make sure that the jsxbin file exists at the desired location. (And of course you have to distribute two files.)
Note. — The separate launcher approach is demonstrated in a technical document provided by Adobe: “Feature Development with Scripting” (PDF). It does not work in CS3. Prior to CS4, any script loaded via doScript
was executed in the main engine —unless it contained a #targetengine
directive,— so it was not possible to load a binary script in a specific engine using doScript
. The solution then was to pass the entire script within a string and to invoke eval
. . . Alas, it's well known by JavaScript gurus that “eval is evil.”
Part 2 - the Bastard Pop Launcher
What Adobe did not tell us, though, is that a jsxbin file is nothing but a serialized ASCII string that we can directly pass to doScript
! Well, almost.
Let's study a stupid JavaScript:
var msg = "Hello, World!"; try { app.selection[0].contents = msg; } catch(_) { alert( msg ); }
Now, paste the JS in ESTK and export it as binary. You get something like this:
The only important thing to note is that the encrypted string contains line breaks. OK, this can be annoying if we intend to use such a string as a literal within a script. But what Adobe did not say either is that the jsxbin line breaks are optional.
Then, open the jsxbin file in your favorite text editor and remove all the line breaks. (Use find/replace on ^p
, \n
, and/or \r
, depending on your application.)
Finally, create a simple JS file and try this:
//----------------------------- // 'Bastard Pop' JSXBIN Launcher //----------------------------- // 1. Create your persistent engine (if needed) #targetengine 'MySessionScript' // 2. Paste the jsxbin code as a literal string in doScript app.doScript("@JSXBIN@ES@2.0@MyBbyBn0ACJAnASzDjNjTjHByBneNiIjFjMjMjPhMhAiXjPjSjMjEhBftgBbyBn0ABJCnABXzIjDjPjOjUjFjOjUjTCfXzBhQDfXzJjTjFjMjFjDjUjJjPjOEfjzDjBjQjQFfVBfyBnfABnzBifGnbyBn0ABJGnAEjzFjBjMjFjSjUHfRBVBfyBffABB40BiAABAzAIByB");
The #targetengine
directive and the jsxbin code are now mixed in a single file, and the inner code is actually executed in the specified engine!
(Works in InDesign CS4 and CS5. In CS3, you can still use eval
on a binary string, it works too —preserving the session engine!)
• See also: “Can InDesign Script Rewrite Itself”
Comments
Hi Marc,
You can also replace the carriage returns with a combination of backslash and CR.
So you can have you string like
var myCode = "@JSXBIN....\
jhjkdhj....\
dscdfds...."
app.doScript(myCode);
This approach is no way better than another one but it may be convenient to have the code displayed as a big block than a single long line. Depends on anyone likes.
To end that comment, I know the ESTK is not the favourite tool for coders but I use it and there is some kind of visual limitations (characters bugs) with long long long strings.
That's a reason why I choose to add backslashes.
Loic
Thanks Loic,
> it may be convenient to have the code displayed as
> a big block than a single long line.
You are perfectly right, except that the jsxbin code is not very interesting to read ;-)
Hey don't believe so :-D
Is there a performance benefit with a jsxbin? I have contacted the author of a script because everything it does is convenient except where it makes the duplicate files and naming. Other than preventing exploitation, why would an author hide the source code of freeware licensed script in a jsxbin? Awaiting a response from author for jsx version that can be customized without intention of any distribution (purely personal/internal use).
@ I_try
> Is there a performance benefit with a jsxbin?
I'd be surprised if so... The JS Binary format is just a way to hide a human-readable source code. I suppose it might have a slight performance cost, in fact.
> Other than preventing exploitation, why would an author hide
> the source code of freeware licensed script in a jsxbin?
Well, I can't answer for him. I guess there are good reasons to distinguish freeware from open source projects. As a programmer I can understand an author wants to protect the originality of an implementation that cost him a lot of work, even if he offers everyone the possibility to use the script.
Generosity does not necessarily mean the abdication of ‘intellectual rights’ —as we say in France ;-)
Regards,
Marc
Hi Marc,
I am trying to use binary code in my another script with your doScript method but when i am using binary code in my another script with doScript method its showing error
Error Number: 30490
Error String: No file is associated with the currently active script.
but when i am running single binary file its working fine.
Whats the problem?
Can you suggest something.
lorena
Hi lorena,
1) Do you use app.activeScript in the source code of the binary script?
2) Show me your doScript statement.
@+
Marc
Is it possible to extract the humanly readable code from the jsxbin file?
Yes it is, but I won't provide any key on that subject ;-)
Hi Marc, I'm interested to hear that it is possible to extract the js code from a jsxbin file. Everyone else I have asked says "no".
As a developer who really wants to protect my methods, I appreciate you not sharing how to do it!
However can you tell me whether it is *easy* or not?
I can live with "really really really difficult" as opposed to "just download this jsxbin decompiler and away you go" :-O
Hi Mike,
My answer is that “it is really really really difficult to decompile a jsxbin.” Thus you can probably live with it so far.
The jxsbin format is a Base64 encoding. Fortunately, Adobe uses some specific conversion rules which makes serious decompilers really hard to achieve. (I found a basic decompiler that only extracts superficial data from a binary script, but it fails to retrieve the logics.)
Apart from that, I'm not allowed to disclose more details on this topic ;-)
@+
Marc
Hi Marc,
I'm a little late to this party, but I can't get CS3 to work at all. Am I doing something incorrectly? My code looks like this:
#target indesign-5.0
eval("@JSXBIN@ES@2.0@MyBbyBn0ABJAnAEjzFjBjMjFjSjUBfRBFeEjUjFjTjUff0DzACByB");
The source for the binary is just alert('test');. If you change the target to indesign-6.0 or greater, it works. I would love to get something like this to work for CS3 though. Any help would be much appreciated. Thanks.
Hi David,
[Sorry for the delay!]
1) What if you remove the #target directive?
2) What if you compile the script from ESTK 2 (@JSXBIN@ES@1.0) rather than ESTK CS4 (@JSXBIN@ES@2.0)?
@+
Marc
Thank you so much Marc! Suggestion #2 worked!