工具简介
Jalangi2是一个动态分析框架,可以用于在JavaScript代码中插入自定义的分析逻辑。它可以捕获代码执行的各种行为,包括函数调用、属性读写、条件语句的执行路径等等。Jalangi2可以用于各种JavaScript代码分析任务,例如安全漏洞检测、性能分析、代码变异测试等等。
Jalangi2的工作流程是通过将目标代码(target code)进行转换,添加钩子成为变换代码(transformed code),从而在执行时允许对代码的每个步骤进行监视和自定义操作。第三方插件(third-party plugin)是由用户编写的分析程序,通过analysis.js进行对代码运行过程中的截获和调用。
命令行使用
- 待分析文件:比较普通的调用了foo和bar两个函数的示例文件。
// target.js
function foo(){
console.log("foo");
return 0;
}
function bar(){
console.log("bar");
}
foo()
bar()
console.log("done");
- 用户提供的分析回调(第三方插件):编写了两个简单的函数,分别在进入函数体和函数返回时调用。
// functionPlugin.js
(function () {
J$.analysis = {
/**
* This callback is called before the execution of a function body starts.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {function} f - The function object whose body is about to get executed
* @param {*} dis - The value of the this variable in the function body
* @param {Array} args - List of the arguments with which the function is called
* @returns {undefined} - Any return value is ignored
*/
functionEnter: function (iid, f, dis, args) {
console.log("Entering function: " + f.name)
},
/**
* This callback is called when the execution of a function body completes.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} returnVal - The value returned by the function
* @param {Object | undefined} wrappedExceptionVal - If this parameter is an object, the function execution has
* thrown an uncaught exception and the exception is being stored in the exception property of the parameter
* @returns {undefined} - Any return value is ignored
*/
functionExit: function (iid, returnVal, wrappedExceptionVal) {
console.log("Function ended with return value: " + returnVal)
},
};
}());
- 命令行调用Jalangi2进行分析:
jalangi2 --inlineIID --inlineSource --analysis functionPlugin.js target.js
执行后目录下会生成target_jalangi_.js和target_jalangi_.json,是经过变换后的代码。
- 输出结果:
Entering function: foo
foo
Function ended with return value: 0
Entering function: bar
bar
Function ended with return value: undefined
done
API使用
使用Jalangi API需要手动调用instrumentCode或instrumentDir函数对目标代码进行变换。待变换完成后,再调用analyze函数对变换后的代码进行分析。API目前只提供了这三个函数。
// runAnalysis.js
const jalangi2 = require("jalangi2")
const runAnalysis = async () => {
await jalangi2.instrumentDir({
inputFiles: ["./target.js"],
outputDir: "./"
});
await jalangi2.analyze("./target.js", ["./functionPlugin.js"])
.then(r => console.log(r))
.catch(e => console.log(e));
}
runAnalysis();
执行instrumentDir函数时,需要在参数options中指定需要变换的输入文件列表inputFiles和输出目录outputDir。函数的返回是一个Promise对象,在异步函数中需要等待变换完成才能执行下一步的分析。
执行analyze函数的参数是变换后的js文件和用户定义的三方插件文件列表,起始参数可选。函数内部实现是间接调用命令行,将参数中的字符串push到命令行的参数中。因此,analyze函数的参数应是文件路径,而不是代码字符串。analyze函数的返回值同样是Promise对象,内容包含返回值、stdout和stderror。
最终执行结果如下:
{
exitCode: 0,
stdout: 'Entering function: foo\n' +
'foo\n' +
'Function ended with return value: 0\n' +
'Entering function: bar\n' +
'bar\n' +
'Function ended with return value: undefined\n' +
'done\n',
stderr: '',
toString: [Function: toString]
}