Life
如果肉已经腐烂,何不忍痛挖掉,即使付出再大代价,也比留下污染更好。 ——— 纪念以前的日子
前奏
很早以前的开发的功能,拿出来分享下吧。
js和jse文件是可以双击直接执行的。根据用户配置的不同,会使用wscript.exe或者cscript.exe来解析。下面所有的分析过程是基于wscript.exe进行的
脚本很简单,就是加载了一个com组件判断下文件在不在。
选择脚本执行引擎
wscript支持vbs脚本和js脚本,那如何确定执行的脚本引擎,答案是通过文件后缀,拿到文件后缀后,查询下对应的progid
这是例子中jse脚本的情况
函数执行完后拿到的progid
加载脚本引擎
有人说,微软针对js和vbs等等,都会自己造轮子。这里不得不说微软自己造轮子的好处,用到的时候方知道。这里不得不说轮子的好处,它可以用一套东西,很方便的去加载不同的引擎,有多方便呢。看下面的代码 上图是为了纪念这个函数
CLSIDFromString的参数,就是mapExtToEngine函数拿到的progid。这里直接通过CoCreateInstance加载相应的dll。dll被加载后,不管是vbs也好,js也好。他们的dll都导出了相同的,IID_IActiveScript和其它的IID。剩下的工作就是操作这些已经统一的IID导出来的接口
就可以很方便的控制脚本的执行。而不用关心这些脚本内部的东西。
amsi初始化
大概说下amsi监控。其实就是一个rpc的发送端和接收端,发送端负责收集脚本的执行情况,发送端是amsi.dll来进行的。脚本运行时会加载这个dll并初始化,执行的时候关键的信息也会调用amsi的接口,发送到接受端进行处理。
上面的progid,JScript.Encode 对应的dll就是jscript.dll。jscript.dll被加载后,第一件事情就是加载amsi,并初始化
首先看下加载的栈
再来近距离欣赏下加载的局部代码(此篇纯粹为了凑字数)
加载过程还是很简单明了,直接Load了amsi.dll,加载完后调用了下AmsiInitialize初始了下。上面说了那么多废话,下面正式进入解析执行阶段
脚本内容解析
还记得上面那个IID_IActiveScriptParse64这个IID吗?本节它就是我们的重点了。除了它父亲遗传给它的那几个接口,自身就有三个
最重要的就是 IActiveScriptParse::ParseScriptText 它了
别看参数这么多,实际上对我们有用的就一个,pstrCode这个参数。看下实际调用的情况
执行
关于代码是如果变成字节码执行的细节,我就不多说了(可能需要一个新的篇幅去介绍),下面看下大概的过程,上面解析完成后,会调用SetScriptState这个函数设置脚本的状态
这个函数是有说明的的,它就是上面一开始看到IID_IActiveScript接口导出的其中一个函数。
这里呢,这个函数的参数是1,代表开始执行
在这个函数内部就直接开始的字节码的执行。把焦点集中在的函数执行中的细节。也就是脚本中的 Scripting.FileSystemObject :: FileExists的调用。
fso.FileExists(filespec)
我们知道执行这个函数的话,最终会调用srcrun.dll里面相应的接口。而执行这个函数的一个最关键的点,就是下面这个函数,VAR类的InvokeByName函数,
为什么这个点是关键呢,因为procmon抓到的日志都是通过这个函数来执行最终的调用,还有就是,我们发现amsi也是在这个里面做了点文章,关于字节码相关的东西,感兴趣可以自行研究下。。。。
通过大量的分析得出,参数 this 是执行类 Scripting.FileSystemObject 的指针,参数a3是函数的名字, 参数a7是参数的个数,参数 a8是倒序存放的参数
再下面呢,就是调用真正的处理函数了。scrrun.dll导出的方法干活,当然这个调用会分为很多步骤,可以看下搜下com 怎么相应的api就可以了。下面只是最开始的一个步骤,说明调用的是这个dll InvokeByName最重要的一个点是,因为它包含了amsi的处理过程,下面正式进入最后的收尾工作,amsi是怎么处理的
amsi监控
amsi的监控和通知,也是在InvokeByName函数中进行的。前提是有人注册amsi的通知,现在假设有人已经注册了
整个amsi的核心,说白了,就是下面这个普普通通的函数,
JAmsi::JAmsiProcessor这个函数的处理过程,分为三步,1. 取类名, 2. 取参数拼成源码形式, 3.检测是否包含执行类的函数,包含则发送,不包含则先存着,等以后再发,下面一步步来看
ida F5后的代码有点乱,但是执行顺序确实是这样的。执行完后Src拿到的就是类的名字。
这一步就是解析函数的参数,其实就是解析,拼装 DISPPARAMS 变量,如果写过com类的调用的话,会熟悉这个变量的。
下面是取函数名,求crc,然后对比,如果是某个关心的函数,则发送到amsi的客户端进行检测。
结束
结尾是一定要突出主题,并照应开头,总结过程的。要不就不是一个好的结尾。