JsSSA与网络请求解析

admin 2024年2月22日18:00:02评论11 views字数 7915阅读26分23秒阅读模式

JsSSA与网络请求解析

前言

当想对浏览器某个页面的的内容进行分析时,通常会进行一个网页跳转的HTTP请求并对服务器响应进行拦截,并获取网页内容,再解析对应的Js代码和Js文件。webpack处理后的js文件中有着各种网络请求。是否有什么方法,能够简单的分析出这些在机器压缩后呈现出的难读的JS代码中的网络请求?

这时就是SSA再次出场的时候了,现在Yakit新增SSA模块解析JavaScript语言,可以使用用JsSSA来实现上述的功能,对webpack打包后的JS中网络请求解析。

JsSSA能做什么

JsSSA与网络请求解析

JavaScript 具有非常棒的模块和方法,可以用来建立可从服务器端资源发送或接收数据的 HTTP 请求。那么如果我们想从JS中整理出这些请求的具体的调用链以及方法中的参数呢?
对JavaScript进行支持并转换成SSA形式,并对JsSSA的调用链进行分析,可以帮助我们从Js网络请求中找出我们想要的东西。

为什么是SSA而不是AST?

JsSSA与网络请求解析

AST——即抽象语法树也可以呈现出程序流程和分析,但为什么不用它?
答案显而易见:效率过低,复杂度高
给一段简单的代码作为例子:

a = ajax.send0a("1111")a = () => {}a("2222")

这段代码会生成这样一棵抽象语法树:

JsSSA与网络请求解析

如果想去寻找“a”的调用,那么每次都要从program这根节点开始搜寻,进入树的分支,再从头开始找,循环往复……
有时可能需要找某种类型的“a”的调用,亦或者这种类型调用的参数,类型和赋值甚至还在两个分支里……
仅仅四行代码的消耗已经可见搜寻AST树的效率之低,代码之复杂,做这样一个难用又难做的东西,实属是不可行。
相比较于如此复杂的AST,SSA显得就先进非常多了:

JsSSA与网络请求解析

在SSA中,每个量都是一个value,它有user和def,即使用它的量和它的定义等,包括参数,类型等等的属性。利用每个量维护的一个value结构,可以轻松找到某个量的调用链,它的类型,使用者以及位置,任何你想要的信息都可以在SSA中维护,而不用在AST中一遍遍地遍历整颗的树还难以找到对应了。
由此,SSA形式是分析的不二之选。

JS网络请求形式

JsSSA与网络请求解析

以最常见的建立异步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来支持分析呢

JsSSA与网络请求解析

目前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来对某个对象进行追踪,获取一个数组。可以通过ShowShowWithSource获取数组信息。

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等。比如,每个值可以通过v.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

 */

结论
JsSSA与网络请求解析

目前Yakit对JsSSA模块的功能和接口支持正在完善中,最终去实现一个能够分析出各类Js网络请求,基于SSA来从分析调用链出发,得到一种类似网络爬虫的解析内容的网络请求分析模块。
最后分析代码如下,首先获取ajax,获取使用者并过滤Field,也就是获得ajax.openajax.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: %sn", v)    v.GetCallArgs().ForEach(v =>{       printf("targument: %sn", 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: falsefunc: phi(ajax)[newActiveXObject("Microsoft.XMLHTTP"),XMLHttpRequest0()].send() */

END

YAK官方资源

Yak 语言官方教程:
https://yaklang.com/docs/intro/
Yakit 视频教程:
https://space.bilibili.com/437503777
Github下载地址:
https://github.com/yaklang/yakit
Yakit官网下载地址:
https://yaklang.com/
Yakit安装文档:
https://yaklang.com/products/download_and_install
Yakit使用文档:
https://yaklang.com/products/intro/
常见问题速查:
https://yaklang.com/products/FAQ

原文始发于微信公众号(Yak Project):JsSSA与网络请求解析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月22日18:00:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JsSSA与网络请求解析https://cn-sec.com/archives/2516184.html

发表评论

匿名网友 填写信息