前言
对java逆向感兴趣的盆友可以关注我以前的文章,有图片验证码识别、aes、sha256等各种加密的java实现,不定时更新常用算法和加密,欢迎一起交流讨论!
在日常逆向中,一些前端的加密代码用java复现出来比较难,所以经常需要调用js文件来实现加密操作,接下来将介绍两种常用调用js的思路,第一种适用于普通js文件,第二种则适用于比较新的v8引擎。在实现的过程中,也会展示可能遇到的问题以及解决办法,废话不多话,正文开始!
方法一
本方法用的是jdk自带的scriptengine
来实现,大概流程是:加载引擎->绑定环境->预编译js文件->调用文件内方法,具体实现代码:
import javax.script.*; import java.io.inputstreamreader; //因为js文件读取一次就行,因此用静态代码块来读取 private static scriptengine engine; static { try { scriptenginemanager manager = new scriptenginemanager(); engine = manager.getenginebyname("javascript"); bindings enginescope = engine.getbindings(scriptcontext.engine_scope); enginescope.put("window", enginescope); enginescope.put("navigator", enginescope); inputstreamreader jsencryptfilereader = new inputstreamreader(encjs.class.getclassloader().getresourceasstream("enc.js")); engine.eval(jsencryptfilereader); jsencryptfilereader.close(); } catch (exception e) { e.printstacktrace(); } }
读取完js后,就可以使用js引擎来直接用invokefunction
方法来调用文件内函数,代码如下
public static string enc(string data, string key) { invocable invoke = (invocable) engine; try { string result = (string) invoke.invokefunction("enc", data, key); return result.touppercase(); } catch (exception e) { e.printstacktrace(); return null; } }
在上边代码invoke.invokefunction("enc", data, key)
中,enc
为js文件内的函数名,data
和key
是需要方法需要传递的参数,如果方法只有一个参数,则传入一个,如果是多个参数,则按顺序排列在后边即可。
上边代码在执行普通js时比较方便使用,但是如果是比较新的开发版本,有些语法会不支持,像lambda表达式等,如下图:
此处再介绍一种方法,使用的是v8引擎,可以支持最新语法
方法二
使用到的maven依赖,以下依赖根据自己环境任选其一即可。
com.eclipsesource.j2v8 j2v8_linux_x86_64 3.1.6 com.eclipsesource.j2v8 j2v8_macosx_x86_64 4.6.0 com.eclipsesource.j2v8 j2v8_win32_x86_64 4.6.0
这个的实现流程和上边稍微有些不同,也是:预读取js文件>加载引擎->编译js文件->调用文件内方法,具体实现代码如下:
import com.eclipsesource.v8.v8; import org.apache.commons.io.ioutils; import java.io.ioexception; import java.nio.charset.standardcharsets; public class v8jsutils { private static string filestr; static { try { filestr = ioutils.tostring(v8jsutils.class.getclassloader().getresourceasstream("enc.js"), standardcharsets.utf_8); } catch (ioexception e) { e.printstacktrace(); } } public static string dec(string data, string key) { v8 runtime = v8.createv8runtime(); try { runtime.executevoidscript(filestr); return (string) runtime.executejsfunction("dec", data, key); } catch (exception e) { e.printstacktrace(); } finally { runtime.release(); } return null; } }
代码写完了,调用一下结果还是遇到了错误
这个错误的大概意思就是:严格模式外不能使用let,const之类的es6的命令,也就是说版本太低了,怎么办,更新版本?no no no,找到js文件出错的代码位置,在方法前加上一句:'use strict';
注意!引号不能省略,必须全部复制,如图
现在再运行就正常了。
附:在java中绑定js变量
在调用engine.get(key);时,如果key没有定义,则返回null
package com.sinaapp.manjushri; import javax.script.bindings; import javax.script.scriptcontext; import javax.script.scriptengine; import javax.script.scriptenginemanager; import javax.script.scriptexception; public class scriptenginetest2 { public static void main(string[] args) { scriptenginemanager manager = new scriptenginemanager(); scriptengine engine = manager.getenginebyname("javascript"); engine.put("a", 4); engine.put("b", 3); bindings bindings = engine.getbindings(scriptcontext.engine_scope); try { // 只能为double,使用float和integer会抛出异常 double result = (double) engine.eval("a b"); system.out.println("result = " result); engine.eval("c=a b"); double c = (double)engine.get("c"); system.out.println("c = " c); } catch (scriptexception e) { e.printstacktrace(); } } }
输出:
result = 7.0
c = 7.0