JSXBIN - Reversing the Binary File Type

JSXBIN

Reversing the Binary File Type

Oliver Kunz
by Oliver Kunz
time to read: 26 minutes

The topic of this week’s Labs is reversing a JSXBIN (JavaScript Binary) file. This article gives you an overview of our analysis and should easily allow you to understand the steps and join in on the work. JSXBIN is a concatenation of JSX and BIN. The first part, JSX, is the default file extension for Adobe ExtendedScript files. That’s basically an extended version of JavaScript. Adobe’s ExtendScript Tookit (ESTK) is an editor to create these scripts and is used in the Adobe Creative Suite (CS). The BIN part of the extension probably refers to binary.

Here is what Adobe has to say about JSXBIN files:

The .jsxbin files are compiled JavaScript; they are in binary format so the source code is not exposed…

According to that document, the use of JSXBIN happens for two reasons:

This labs will demonstrate, that JSXBIN files are nothing but source code obfuscated JSX files. With some effort the source is recognizable and with some more effort it might even be possible to completely reverse the JSXBIN to a JSX.

How to create a JSXBIN file

After installing the ESTK, let’s have a look at how to create a binary version of a JSX file.

The binary, that isn’t binary

Open your newly created JSXBIN file in a file editor (e.g. notepad or notepad++). The first thing for you to notice: It’s not really binary in the sense of a compiled executionable. In fact, it’s all ASCII limited to the characters [A-Za-z0-9]. It might remind you of base64 encoding. If you like, go on and try to decode it as base64. Because if we look at the meaning and method of the code, it is somewhat similar to base64. It uses the same character set to represent the original JSX file. It is definitively not binary.

Like I said, a JSXBIN is nothing but code obfuscation. For those of you interested in code obfuscation, there are contests to write the best obfuscated code or break a piece of obfuscated code. However, it is now clear, that the first purpose – to protect the source code from exposure – is certainly not true. It only takes time to crack the obfuscation.

The good news for us, we have the ESTK to create the obfuscation in any plaintext we like. That’s exactly what my first approach was in reversing a JSXBIN. And here are the results.

The Starting Point

Let’s forget about the previously created JSXBIN file for a second and start from scratch. The first script we’re going to export is an empty JSX file. This gives us information about possible static content.

The following is all the content of a JSXBIN from an empty JSX script.

@JSXBIN@ES@2.0@MyBn0DzABByB

For a next export, enter a single letter (e.g. the lower case a) in the JSX script file and export that script. You will receive the following JSXBIN content:

@JSXBIN@ES@2.0@MyBbyBn0ABJAnAjzBjBBf0DzACByB

Repeat this by yourself and after many more JSXBIN files, we are able to identify somewhat static parts.

The Next Steps

We have identified what seems to be a description header and a possible static three letter block at the beginning and end of the JSXBIN file. The next steps are to identify the representation of different characters and symbols, and their combinations.

For example extend the above one letter script to with an additional lower case a and export the script aa, followed by aaa and so on. After that, create the same for any other letter, and then write each on a different line and so on. After identifying the character representation, start identifying objects, variables, functions, control structures etc. And then the combination of all of those.

Alphabet Representation

Having exported many scripts of various combinations of letters, the following letter representation characteristic was identified. A lower case letter (not as a data type string) is represented by j[B-Za]. Starting with a = jB and ending with z = ja. The upper case letters differ only in the leading value. Instead of j, a lower case i is used. This gives A = iB and Z = ia.

In addition to the representation of lower and upper case letters, a leading value that represents the number of letters was identified. For a single letter like a, that was a B, for two letters such as aa, that was a C. This goes on, up the alphabet and will be discussed in more details in the section about counter values.

Lower Case Letter Representation Upper Case Letter Representation
a jB A iB
b jC B iC
c jD C iD
d jE D iE
e jF E iF
f jG F iG
g jH G iH
h jI H iI
i jJ I iJ
j jK J iK
k jL K iL
l jM L iM
m jN M iN
n jO N iO
o jP O iP
p jQ P iQ
q jR Q iR
r jS R iS
s jT S iT
t jU T iU
u jV U iV
v jW V iW
w jX W iX
x jY X iY
y jZ Y iZ
z ja Z ia

The table is true for variable names, function names etc. In case of the data type string, the letters are represented in the same form. Before the leading letter counter value, a new identifier is inserted – Fe – to represent that the next letters and characters are part of a string. We are now able to translate the value Test to the JSXBIN representation EiUjFjTjU (including the counter value E). The same value as a data type string “Test” is represented as FeEiUjFjTjU (including string marker Fe and counter E).

Special Characters Representation

The table below shows some special characters recognised during the analysis.

Special Character
(in string)
Description Representation
“ “ Space hA
Quotation Mark hC
Apostrophe hH
& Ampersand hG
\ Backslash ic
: Colon ha
. Period hO
Dash hN
_ Underscore if
? Question Mark hf
^ Caret ie
` Accent Grave ja
~ Tilde je
ยด Accent Aigu lU
+ Plus hL
* Asterisk hK
# Hash hD
% Percentage sign hF
/ Slash hP
| Pipe jc
( Parenthesis Open hI
) Parenthesis Close jJ
> Is Greater Than hc
< Is Less Than he

We are now able to represent the string “Test Value” in JSXBIN as FeKiUjFjTjUhAiWjBjMjVjF.

Digits Representation

We have identified lower and upper case letters and special characters, as well as the difference between functional strings (variable, function etc. names) and the data type strings. In a next step, we are going to analyse the representation of digits and numbers as numerical values and as part in the data type string.

The procedure is same as the process to identify the letter values. First, there’s an export of a JSX script with a single digit, then multiple digits on the same line and the same digit on different lines.

The first export is of the script with a single digit 1 (remember, we’re removing the static description header from the output).

MyBbyBn0ABJAnAFdB0EzABByB

In the above export, the digit 1 is represented by dB. Different than with the upper and lower case letters, the string “1” is represented by a different value, hR. The table below shows the digit, its representation as number and its representation in a string data type.

Digit Character Representation Numerical String Representation
0 d (dA – unbestätigt) “0” hQ
1 dB “1” hR
2 dC “2” hS
3 dD “3” hT
4 dE “4” hU
5 dF “5” hV
6 dG “6” hW
7 dH “7” hX
8 dI “8” hY
9 dJ “9” hZ
10 dK “10” hRhQ
11 dL “11” hRhR
12 dM “12” hRhS
13 dN “13” hRhT
14 dO “14” hRhU
15 dP “15” hRhV
16 dQ “16” hRhW
17 dR “17” hRhX
18 dS “18” hRhY
19 dT “19” hRhZ
20 dU “20” hShQ
21 dV “21” hShR
22 dW “22” hShS
23 dX “23” hShT
24 dY “24” hShU
25 dZ “25” hShV
26 dga “26” hShW
27 dgb “27” hShX
28 dgc “28” hShY
29 dgd “29” hShZ
30 dge “30” hShQ
31 dgf “31” hShR
32 dhA “32” hShS
33 dhB “33” hShT

Note: The table only lists the values for the digits/numbers and does not contain any form of counter value.

We can see a clear difference between the digit character and the numerical string equivalent. The latter is just a combination of the string values from 0-9. This means the string “22192” is represented as FeFhShShZhRhS (including string marker Fe and counter value F). The number 22192 as a number acts like a counter (see section below) and is represented as d2kAiZ.

Counter Values

Anatomy of a word in JSXBIN

Counters are found in different functions. They may count the number of characters used in the name of a variable of function, the length of a string, there might even exist some sort of line numbers/counters. To my understanding, not all counters have the same starting value. What all have in common is the overflow after a multiple of 32.

Character Counters

We have already introduced the counter value in this article. Time to expand on that. A single lowercase letter is substituted with j[A-Za], excluding the counter value. If we add said counter value to it – the character indicating how long the string that follows is – the single lowercase letter a will appear in the JSXBIN as BjB. Let’s have a look at an example, namely the word Test, which is a functional string or a string data type. The word Test is a four-letter word and we have deduced that the leading counter value for four-letter-words is E. Therefore, we can state that the leading counter value character of a single-letter string is B and if we are trying to decode a four-letter character combination, then it’s E.

Line Counters

Now let’s try something else. Write the same characters on multiple lines, create a JSXBIN and try to find the value that represents a line break.

Here is an example of a multi-line file:

"line1"
"line2"
"line3"

And its JSXBIN representation (some form formatting was applied to outline the different lines and blocks):

MyBbyBn0AD
("line1")	JAnA Fe FjMjJjOjFhR
("line2")	JBnA Fe FjMjJjOjFhS
("line3")	JCnA Fe FjMjJjOjFhT
0DzABByB

Let’s ignore the first and the last line and concentrate on the three lines in between. The string representation of “line[1-3]” is FeFjMjJjOjFh[R-T]. In order to identify the line counter, we can focus on the first four characters. The first four characters are of the form J[A-C]nA. If there is a line counter or line number value, it has to be the second value of that four character block.

The next example is an export of a file with 33 lines. We are only looking at the last four identified lines of the JSXBIN string (it is only an excerpt of the important lines to express the overflow happening between 26 and 27, and 32 and 33. Plus some formatting is applied for better explanation).

("line26")	J Z  nA Fe GjMjJjOjFhShW
("line27")	J ga nA Fe GjMjJjOjFhShX
("line32")	J gf nA Fe GjMjJjOjFhThS
("line33")	J hA nA Fe GjMjJjOjFhThT

Again, focusing on the characters from the beginning to the string data type delimiter (Fe), we are able to see that the first letter (J) remains static. It is followed by the counter value that can be comprised of either one or two characters. The block is ended with two static characters (nA).

We are able to identify that after the number 26 two characters are used to represent 27 and from line 32 to 33 an overflow occurs.

Here is what’s happening if we only look at the binary representation of the counters:

Z	01011010
ga	01100111 01100001
gf	01100111 01100110
hA	01101000 01000001

But why isn’t it just 01011011 followed by 01011100 after Z? The first byte represents [ in ASCII, the second represents \. So it is for the simple reason that only [A-Za-z0-9] are used in the JSXBIN file.

The Counter Table

In the following table, you will find the counter values until the first overflow and some more overflow examples.

Counter Value Representation
0 A (only an assumption)
1 B
2 C
3 D
4 E
5 F
6 G
7 H
8 I
9 J
10 K
11 L
12 M
19 T
20 U
21 V
22 W
23 X
24 Y
25 Z
26 ga
27 gb
28 gc
29 gd
30 ge
31 gf
32 hA
63 hf
64 iA
95 if
96 jA
127 jf
128 kA

Variables

In the previous section, we identified the representation of different characters, the representation in different cases (string data type or not) and we identified possible numbering structures or counters.

In this section, we are going to look at variable declaration. You could start again and build up the declaration. It’s what was done for this article, but we skip that and go straight to a more complex example.

var a=2;
a=1;
a;
var b=null;
b=1;
var test=1;
test = a;

The JSX script is represented (in a formatted form to identify elements) below.

MyB byB n0AG 
a)	JAnA Sz BjB ByB ndCft
b)	JBnA S ByB ndBff
c)	JCnA V BfyB
d)	JDnA Sz BjC CyB nbft
e)	JEnA S CyB ndBff
f)	JFnA Sz EjUjFjTjU DyB ndBft 
g)	JGnA S DyB V BfyB nff
AD B40Bi AC 4B0Ai AD 4C0Ai AAD 
AzAE ByB
Line Block #1 Block #2 Block #3 Block #4 Block #5 Block #6
a) JAnA Sz BjB ByB ndCft
A (see section Counter Values) Sz observed when variable is declared and defined in one construct see section Alphabet Representation and Counter Values ByB possibly a reference dC represents the digit 2
b) JBnA S ByB ndBff
B (see section Counter Values) S observed when a declared variable is assigned a new value ByB possibly a reference dB represents the digit 1
c) JCnA V BfyB
C (see section Counter Values) V observed when a variable is referenced BfyB is very similar to ByB this could possibly be a reference to the variable data
g) JGnA S DyB V BfyB nff
G (see section Counter Values) S observed when a declared variable is assigned a new value DyB possibly the reference to the variable test V observed when a variable is referenced ByfB could possibly be a reference to the variable data nff remains unknown, but very close to the last block in line b) (if dB is removed)

The line below g) (AD B40Bi AC 4B0Ai AD 4C0Ai AAD) is of importance, but it’s meaning is unknown as of yet. A similar line always appears in the case of variable usage.

Functions

We are going to analyse functions in this section. Beginning with empty functions, and adding more context later in the analysis.

Empty functions

We start with empty, one-liner functions.

function a(){}
function b(){}
function c(){}

This is the converted JSXBIN content.

MyB byB nAD
		MAn0 Dz BjB B0A
		MBn0 Dz BjC CAB
		MCn0 Dz BjD DAC
0EzAE ByB

Looking at this example, we might be able to identify some of the elements.

Next, we will have a look at two empty functions, but this time declared over multiple lines.

1) function a(){
2)    
3)    
4)    
5)    
6)    }
7) function b(){
8)    
9)    
10)    
11)    
12)   }

Note: The line numbers are added for visibility and not part of the JSX script.

The JSX script is represented in JSXBIN by (Line designations a) and b) are added for visibility):

MyB byB nAC
a)		MAn0 Dz BjB BAF
b)		MGn0 Dz BjC CAL
0EzAD ByB

If we compare line a) with the first function line (MAn0 Dz BjB B0A) of the first example, we see that the last two letters changed. If we compare line b) and the second function line (MBn0 Dz BjC CAB) of the first example, we see the second and the last letter changed. The only difference in the source code is that there are empty lines.

Hence, the second letter might declare the line number where the function declaration starts (function a(){ and the last letter defines the line number where the function declaration ends (}).

Function Calls

Before looking to more complex function definitions, here is an example with multiple examples and that calls more than one function.

function xxa(){}
xxa();
function bbb(){}
bbb();
function ccc(){}
ccc();
function ddd(){}
eee();
function eee(){}
ddd();
xxa();
bbb();
ccc();
ddd();
eee();
xxa();
bbb();
ccc();
ddd();
eee();
xxa();
bbb();
ccc();
ddd();
eee();

The converted JSXBIN string:

MyB byB nAF
	MAn0 Dz DjYjYjB B0A
	MCn0 Dz DjCjCjC CAC
	MEn0 Dz DjDjDjD DAE
	MGn0 Dz DjEjEjE EAG
	MIn0 Dz DjFjFjF FAI 
U
	JBnA EjB fnf
	JDnA EjC fnf
	JFnA EjD fnf
	JHnA EjF fnf
	JJnA EjE fnf
	JKnA EjB fnf
	JLnA EjC fnf
	JMnA EjD fnf
	JNnA EjE fnf
	JOnA EjF fnf
	JPnA EjB fnf
	JQnA EjC fnf
	JRnA EjD fnf
	JSnA EjE fnf
	JTnA EjF fnf
	JUnA EjB fnf
	JVnA EjC fnf
	JWnA EjD fnf
	JXnA EjE fnf
	JYnA EjF fnf
0DzAG ByB

By analysing the JSXBIN string, we are able to identify that the function calls between the function declarations are shifted down. As a consequence all function declarations are bundled at the beginning of the JSXBIN string (have a look at the second letter after M or J indicating the line number).

We are also able to identify the structure of a function call.

If we have a closer look at the second block and take the first function call (xxa()) as example. The definition of the function xxa() equals the line MAn0 Dz DjYjYjB B0A and the call of the function xxa() equals the line JBnA EjB fnf. Note that the first letter of the last block in the function declaration line equals the last letter in the second block of the function calling line (here B). Therefore that seems to be the reference value and Ej must be some sort of generic function call operation.

Multi-Line Functions

As outlined in the beginning of this section, we will now analyse functions with more context. We will see that certain elements from the section above are changing and new elements are inserted.

1)	function a(){
2)		//test1
3)		"alpha"
4)		//test2    
5)		}

Note: The numbers 1-5 are inserted for visibility reasons and not part of the actual content.

This is the (slightly formatted) JSXBIN output string:

MyB byB nAB
a)	MA byB n0 AB
b)	JCnA FeFjBjMjQjIjB 0 
c)	Dz BjB BAE
0EzACByB

If we compare line a) with the equivalent line of the first example (MAn0 Dz BjB BAF) we are able to identify quite some strong differences.

Two other details to note:

Line c) contains the remaining parts of the function declaration of the first example (MAn0 Dz BjB BAF). The last letter F is equivalent with the number 5, which equals the line number where the function declaration is finished.

Now let’s look at an example where the function is called on a different line below the declaration.

1) function a(){
2)     //test1
3)     "alpha"
4)     //test2    
5)     }
6)
7) a();

Note: The number 1-5 are inserted for visibility reasons and not part of the actual content

Transformed to JSXBIN:

MyB byB nAB
a)	MA byB n0 AB
b)	JCnA FeFjBjMjQjIjB 0
c)	Dz BjB BAE
d)	B
e)	JGnA EjB fnf 
0DzAC ByB

So far our examples are very easy and don’t have a lot of content. We will now extend the function by a variable declaration.

1) function a(){
2)		//test1
3)		var non=null;
4)		//test2
5)		}

The converted JSXBIN:

MyB byB nAB
a)	MA byB n0 AB
b)	JCnA Sz DjOjPjO BA nbft
c)	AB B40Bi AAB
d)	Az BjB CAE 
0EzAD ByB

The line b) equals the variable declaration (see section Variables for more details). As mentioned at the end of the section Variables, there is a line that is still unknown in functionality. A similar line appears here as line c). Another difference could be seen in line d), what before was Dz turned into Az with the declaration of a variable.

We extend the last example and add another variable to the function.

1) function a(){
2)     //test1
3)     var non=null;
4)     var alpha="alpha";    
5)     //test2
6)	  }

The converted JSXBIN:

MyB byB nAB
a)	MA byB n0AC
b)	JCnA Sz DjOjPjO BA nbft
c)	JDnA Sz FjBjMjQjIj BCB neFjBjMjQjIjB ft
d)	AC C 4B0Ai A B40Bi AAC 
e)	Az BjB DAF
0EzAE ByB

In this example, the line c) is the newly added variable alpha with the assigned value “alpha”. The added variable also affects line d), the old one was AB B40Bi AAB and the new one is AC C 4B0Ai A B40Bi AAC.

Conclusion Function

There is certainly much going on in the conversion of a function from JSX to JSXBIN. Reversing the full algorithm would take much more time. This is especially because of the unrecognized parts in the variable declaration.

To summarize what I have got so far:

Not Printed Values

Some parts are not represented specifically represented in a JSXBIN string. Among others such values are:

Conclusion

For the time being, this is the end of the reversing of the JSX to JSXBIN export functionality. With the functionality described in this article, it is possible to write a simple script converting the identified elements back into JSX. It may be possible that some elements are replaced with the wrong values. This is due to the script being more of something that identifies strings rather than a complete re-converter.

This article was posted as detailed as necessary to leave it open for anyone to contribute their own analysis. There is still much to do, apart form the variable and function declaration. For example: control structures (for, while etc.) and mathematical operations.

About the Author

Oliver Kunz

Oliver Kunz has been in information security since 2010. Mainly, he deals with incident response, forensics and the security of mobile devices.

Links

You need support in such a project?

Our experts will get in contact with you!

×
Security Testing

Security Testing

Tomaso Vasella

Active Directory certificate services

Active Directory certificate services

Eric Maurer

Foreign Entra Workload Identities

Foreign Entra Workload Identities

Marius Elmiger

Active Directory certificate services

Active Directory certificate services

Eric Maurer

You want more?

Further articles available here

You need support in such a project?

Our experts will get in contact with you!

You want more?

Further articles available here