B4J Tutorial Application protection

This is at present a proof of concept, but does allow applications written in B4J to be protected.

It relies on my Nashorn library.

The way it works is as follows:

The B4J application pulls in an AES encrypted javascript file from a source (disk or web).
It decodes it, and then allows for execution of functions held within.

If the incorrect key is used , the script file fails to decode and the application can handle the outcome.

Variables within the B4J application can be set from within the javascript (database credentials etc)
The interface between javascript and B4j is really simple.

The main protection comes from the fact the executable javascript only exists at runtime, so disassembling the program will not reveal the code in the javascript.

In the example attached, encryption and decryption is handled in the one app. Obviously you would not have the script encryption and decryption in the same application in a real world application.

B4X:
Sub Process_Globals
 Private fx As JFX
 Private MainForm As Form
 Dim script As String
 Dim nash As jInvokeNashorn
 Dim enc As Cipher
 Dim k As KeyGenerator
End Sub
Sub AppStart (Form1 As Form, Args() As String)
 MainForm = Form1
 'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
 MainForm.Show
 enc.Initialize("AES")
 ' make the script
 script = $" 
 var b4j = Java.type("b4j.example.main") // this could be 'Me' passed from B4J
 function test(){
 print("hello from script")
 b4j._hello() // this calls the sub hello directly from the script
 }"$
 ' initialize the key this would be secret normally or passphrase
 k.Initialize("AES")
 k.KeyFromBytes("1234567890123456".GetBytes("utf8"))
 ' encrypt the script and save to disk
 Dim eScript() As Byte = enc.Encrypt(script.GetBytes("utf8"),k.Key,False)
 Dim out As OutputStream = File.OpenOutput("C:/temp","cipher.key",False)
 out.WriteBytes(eScript,0,eScript.Length)
 out.Flush
 out.Close
 ' read the encrypted file and decode it
 k.KeyFromBytes("1234567890123456".GetBytes("utf8"))  ' try changing key here from correct one
 Dim inp As InputStream = File.OpenInput("C:/temp","cipher.key")
 Dim decodedScript(inp.BytesAvailable) As Byte
 inp.ReadBytes(decodedScript,0,inp.BytesAvailable)
 inp.Close
 Try 
 Dim dString() As Byte = enc.Decrypt(decodedScript,k.Key,False)
 ' initialize nashorn on the script
 nash.InitInvocable(BytesToString(dString,0,dString.Length,"utf8"))
 ' invoke the function in the script
 nash.Invoke("test",Null)
 Catch
 Log("You are an invalid user and cannot access the full program")
 End Try 
End Sub
' just a sub called from the script
Sub hello()
 Log("hello from main")
End Sub

I have used AES encryption, but it can be easily changed to any other supported encryption.
 

Daestrum

Expert
Licensed User
Longtime User
Added handling of Obsfuscated names so you can just refer to the sub name you used instead of the obsfuscated name.
 

Daestrum

Expert
Licensed User
Longtime User
The code to deobsfuscate the b4j sub names (forgot to add it last time)

B4X:
Sub Process_Globals
Private fx As JFX
Private MainForm As Form
Dim script As String
Dim nash As jInvokeNashorn
Dim enc As Cipher
Dim k As KeyGenerator
End Sub
Sub AppStart (Form1 As Form, Args() As String)
MainForm = Form1
'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
MainForm.Show
enc.Initialize("AES")
' make the script
script = $"
var b4j = Java.type("b4j.example.main")
var HashMap = Java.type("java.util.HashMap")
var myMap = new HashMap()
var obs = false

function deObs(){
// change the next line to point to the map file
var filename = "C:/b4j Problems/encrypted js file/Objects/ObfuscatorMap.txt"
if(b4j._read_file(filename)==""){
return
}
obs = !obs
for each(line in b4j._read_file(filename).split("\r\n")){
if (line.contains('=')){
var data = line.split("=")
myMap.put(data[1],data[0])
}
}
print("map >>"+myMap)
}
// call b4j sub
function b4jSub(a,b){
if (b!=null){
obs!=false?b4j["_"+myMap.get(a)](b):b4j["_"+a](b)
} else {
obs!=false?b4j["_"+myMap.get(a)]():b4j["_"+a]()
}
}

function test(){
print("hello from script")
b4jSub("hello")
b4jSub("hello2",["freddy","mike","jimmy"])
}"$
' initialize the key this would be secret normally or passphrase
k.Initialize("AES")
k.KeyFromBytes("1234567890123456".GetBytes("utf8"))
'k.KeyFromBytes("1234567890123456".GetBytes("utf8"))
' encrypt the script and save to disk
Dim eScript() As Byte = enc.Encrypt(script.GetBytes("utf8"),k.Key,False)
Dim out As OutputStream = File.OpenOutput("C:/temp","cipher.key",False)
out.WriteBytes(eScript,0,eScript.Length)
out.Flush
out.Close
' read the encrypted file and decode it
k.KeyFromBytes(read_File("C:/temp/secretkey.txt").Replace(Chr(13),"").Replace(Chr(10),"").getBytes("utf8"))
'k.KeyFromBytes("1234567890123456".GetBytes("utf8"))  ' try changing key here from correct one
Dim inp As InputStream = File.OpenInput("C:/temp","cipher.key")
Dim decodedScript(inp.BytesAvailable) As Byte
inp.ReadBytes(decodedScript,0,inp.BytesAvailable)
inp.Close
Try
Dim dString() As Byte = enc.Decrypt(decodedScript,k.Key,False)
' initialize nashorn on the script
'nash.Debug=True << this line only works in my copy of jInvokeNashorn
nash.InitInvocable(BytesToString(dString,0,dString.Length,"utf8"))
' invoke the function in the script
nash.Invoke("deObs",Null)
nash.Invoke("test",Null)
Catch
Log("You are an invalid user and cannot access the full program")
Log(LastException)
End Try
End Sub
Sub read_File(name As String) As String
Try
Return File.ReadString("",name)
Catch
Return ""
End Try
End Sub
' just a sub called from the script
Sub hello()
Log("hello from main")
End Sub
Sub hello2(s() As String)
Log("main with params:")
For Each ss As String In s
Log(ss)
Next
End Sub
 
Last edited:
Top