前言
当想对浏览器某个页面的的内容进行分析时,通常会进行一个网页跳转的HTTP请求并对服务器响应进行拦截,并获取网页内容,再解析对应的Js代码和Js文件。webpack处理后的js文件中有着各种网络请求。是否有什么方法,能够简单的分析出这些在机器压缩后呈现出的难读的JS代码中的网络请求?
这时就是SSA再次出场的时候了,现在Yakit新增SSA模块解析JavaScript语言,可以使用用JsSSA来实现上述的功能,对webpack打包后的JS中网络请求解析。
JsSSA能做什么
JavaScript 具有非常棒的模块和方法,可以用来建立可从服务器端资源发送或接收数据的 HTTP 请求。那么如果我们想从JS中整理出这些请求的具体的调用链以及方法中的参数呢?
对JavaScript进行支持并转换成SSA形式,并对JsSSA的调用链进行分析,可以帮助我们从Js网络请求中找出我们想要的东西。
为什么是SSA而不是AST?
AST——即抽象语法树也可以呈现出程序流程和分析,但为什么不用它?
答案显而易见:效率过低,复杂度高
给一段简单的代码作为例子:
a = ajax.send0 a("1111") a = () => {} a("2222")
这段代码会生成这样一棵抽象语法树:
如果想去寻找“a”的调用,那么每次都要从program这根节点开始搜寻,进入树的分支,再从头开始找,循环往复……
有时可能需要找某种类型的“a”的调用,亦或者这种类型调用的参数,类型和赋值甚至还在两个分支里……
仅仅四行代码的消耗已经可见搜寻AST树的效率之低,代码之复杂,做这样一个难用又难做的东西,实属是不可行。
相比较于如此复杂的AST,SSA显得就先进非常多了:
在SSA中,每个量都是一个value,它有user和def,即使用它的量和它的定义等,包括参数,类型等等的属性。利用每个量维护的一个value结构,可以轻松找到某个量的调用链,它的类型,使用者以及位置,任何你想要的信息都可以在SSA中维护,而不用在AST中一遍遍地遍历整颗的树还难以找到对应了。
由此,SSA形式是分析的不二之选。
JS网络请求形式
以最常见的建立异步HTTP的请求的方式Ajax为例,该方法可以使用 HTTP-POST 方法来发送数据,以及使用 HTTP-GET 来接收数据。
要在 Ajax 中发起一个 HTTP 调用,需要初始化一个新的XMLHttpRequest()方法,指定 URL 端点和 HTTP 方法。最后,使用open()方法将两者结合起来,并调用send()方法执行请求。
if (window.ActiveXObject) { ajax = newActiveXObject("Microsoft.XMLHTTP") }else{ ajax = new XMLHttpRequest0; } ajax.open('post', "ajax_link.php?id=1&t=" + Math.random, false) ajax.send()
这段代码中使用了GET方法来对baidu网站构建了请求,并使用send发送。如果想知道JS文件中所有的XMLHttpRequest()都请求了什么url,使用了什么方法,这时候就是JsSSA模块使用的时候了。
有哪些API来支持分析呢
目前API功能仍需要完善,但已经有了基础的功能,可以实现较为简单的解析和查看相关的调用链。
使用Parse对代码进行解析得到ssaapi.Program对象
对整个code的解析只有一个函数Parse,传入code参数以及其他可选参数进行解析:
ssa.Parse(code /*type: string*/, opts...) (*ssaapi.Program, error) opt: ssa.withLanguage: arg: ssa.Javascript ssa.Yak (default)
将需要处理的代码进行解析:
prog := ssa.Parse(` if (window.ActiveXObject) { ajax = newActiveXObject("Microsoft.XMLHTTP") }else{ ajax = new XMLHttpRequest0; } ajax.open('post', "ajax_link.php?id=1&t=" + Math.random, false) ajax.send() `, ssa.withLanguage(ssa.Javascript))~
对ssaapi.Program使用Ref获取变量
使用Ref来对某个对象进行追踪,获取一个数组。可以通过Show或ShowWithSource获取数组信息。
ajax = prog.Ref("ajax").Show() /* Values: 3 0: Call: newActiveXObject("Microsoft.XMLHTTP") 1: Call: XMLHttpRequest0() 2: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] */
可以看到获取到的数据有三个,其中有两个函数调用,分别是if语句中的两个分支。
在运行时程序将会运行If中的一个分支,也就具体为某一个值,但是在静态分析中,我们通过Phi来表示多个数据的聚合。可以看到在以上的代码中If运行结束以后,ajax会成为newActiveXObject("Microsoft.XMLHTTP")或new XMLHttpRequest0, 也就表示为phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()]。
对ssaapi.Values的操作:
使用ForEach遍历
使用ForEach可以遍历整个值的数组,并获取每一个值。并使用ShowUseDefChain 可以获取值的一层的引用关系:
prog := ssa.Parse(` if (window.ActiveXObject) { ajax = newActiveXObject("Microsoft.XMLHTTP") }else{ ajax = new XMLHttpRequest0; } ajax.open('post', "ajax_link.php?id=1&t=" + Math.random, false) ajax.send() `, ssa.withLanguage(ssa.Javascript))~ ajax = prog.Ref("ajax").ForEach( v => v.ShowUseDefChain() )
查看open/send0方法的usedefchain:
可以查看通过ref获取的变量值,每个会单独打印一个表格,每一行表示一个相关的值,其中每行包含以下信息:
- 该值的类型,表示为 Self表示Ref获取到的值本身,Operand表示Self使用的值,User表示使用Self的值,
- 该值的索引,任何一个值都可以通过GetOperand(index)和GetUser(index)获取到指定的值,参数中的index和此处表示的索引一致。
- 该值的Opcode,可以理解为值的行为。常见的如 函数调用Call, 数值运算BinOp等。比如,每个值可以通过IsCall()判断是否为函数调用。
- 该值的单行打印,将会把整个值打印为一行。
比如以上代码中运行以后的数值如下,相关解释已经在注释中:
use-def: |Type |index |Opcode |Value Operand 0 Undefined newActiveXObject Operand 1 ConstInst "Microsoft.XMLHTTP" Self Call newActiveXObject("Microsoft.XMLHTTP") User 0 Phi phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] use-def: |Type |index |Opcode |Value Operand 0 Undefined XMLHttpRequest0 Self Call XMLHttpRequest0() User 0 Phi phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] use-def: |Type |index |Opcode |Value Operand 0 Call newActiveXObject("Microsoft.XMLHTTP") Operand 1 Call XMLHttpRequest0() Self Phi phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] // phi是出现数据交汇时产生的值,表示在运行时将会得到Phi中显示的所有值中的一个。 User 0 Field phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open User 1 Field phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send
使用GetUsers获取使用者
对于ForEach中的单个Value可以使用GetUsers可以获取所有的User返回一个value数组。
对于value数组也可以使用GetUsers,对其中每个值进行GetUsers并返回所有的User。
比如一下代码,可以参考ShowUseDefChain后的数据进行比对。
prog := ssa.Parse(` if (window.ActiveXObject) { ajax = newActiveXObject("Microsoft.XMLHTTP") }else{ ajax = new XMLHttpRequest0; } ajax.open('post', "ajax_link.php?id=1&t=" + Math.random, false) ajax.send() `, ssa.withLanguage(ssa.Javascript))~ ajax = prog.Ref("ajax").Show() /* Values: 3 0: Call: newActiveXObject("Microsoft.XMLHTTP") 1: Call: XMLHttpRequest0() 2: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] */ ajaxUser = ajax.GetUsers().Show() /* Values: 4 0: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] 1: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] 2: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open 3: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send */
使用Filter过滤值
Filter可以对值的数组进行过滤,该函数类似ForEach函数,但将会返回一个bool类型,以判断当前值是否继续保存。比如以下的示例,将会只留下Field类型的数据。
prog := ssa.Parse(` if (window.ActiveXObject) { ajax = newActiveXObject("Microsoft.XMLHTTP") }else{ ajax = new XMLHttpRequest0; } ajax.open('post', "ajax_link.php?id=1&t=" + Math.random, false) ajax.send() `, ssa.withLanguage(ssa.Javascript))~ ajax = prog.Ref("ajax").Show() /* Values: 3 0: Call: newActiveXObject("Microsoft.XMLHTTP") 1: Call: XMLHttpRequest0() 2: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] */ ajaxUser = ajax.GetUsers().Show() /* Values: 4 0: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] 1: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] 2: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open 3: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send */ ajaxFunc = ajaxUser.Filter(v => v.IsField()).Show() /* Values: 2 0: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open 1: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send */
结论
目前Yakit对JsSSA模块的功能和接口支持正在完善中,最终去实现一个能够分析出各类Js网络请求,基于SSA来从分析调用链出发,得到一种类似网络爬虫的解析内容的网络请求分析模块。
最后分析代码如下,首先获取ajax,获取使用者并过滤Field,也就是获得ajax.open和ajax.send, 继续获取使用者并过滤Call,也就得到了对于两个函数的调用,直接打印函数以及参数信息。
prog := ssa.Parse(` if (window.ActiveXObject) { ajax = newActiveXObject("Microsoft.XMLHTTP") }else{ ajax = new XMLHttpRequest0; } ajax.open('post', "ajax_link.php?id=1&t=" + Math.random, false) ajax.send() `, ssa.withLanguage(ssa.Javascript))~ ajax = prog.Ref("ajax").Show() /* Values: 3 0: Call: newActiveXObject("Microsoft.XMLHTTP") 1: Call: XMLHttpRequest0() 2: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] */ ajaxUser = ajax.GetUsers().Show() /* Values: 4 0: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] 1: Phi: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()] 2: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open 3: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send */ ajaxFunc = ajaxUser.Filter(v => v.IsField()).Show() /* Values: 2 0: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open 1: Field: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send */ ajaxFuncCaller = ajaxFunc.GetUsers().Filter(v => v.IsCall()).Show() /* Values: 2 0: Call: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open("post",add("ajax_link.php?id=1&t=", Math.random),false) 1: Call: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send() */ ajaxFuncCaller.ForEach(v =>{ printf("func: %s\n", v) v.GetCallArgs().ForEach(v =>{ printf("\targument: %s\n", v) }) }) /* func: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].open("post",add("ajax_link.php?id=1&t=", Math.random),false) argument: "post" argument: add("ajax_link.php?id=1&t=", Math.random) argument: false func: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send() */
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)