前言:
 去年自学了部分关于宏病毒的知识,一直忙于学新知识,转眼又过了年,年前没能汇总、整理、分享宏病毒相关的笔记与心得,这次通过两个样本聊聊常见的宏病毒。

样本一 样本二
加密混淆(附C++去混淆代码) 加密混淆

宏病毒:最常见的就是微软的office(Word宏),欺骗受害者点击文档(伪装),运行宏代码,从而感染了Word宏病毒,通常宏病毒是作为下载器来传播的。
 个人感觉最好的办法是禁用宏呗,一旦打开Word,有宏运行,那么就会提示禁止了宏无法运行(选项中的安全级别可以设置宏的安全等级)。

宏编程语言(WORD):VBA 就是嵌入应用程序(WORD)中的VB 语法与vb一样,只是它有特定对象、方法、属性,写出的代码不能脱离word环境。

宏的爱好::宏病毒为了避免杀毒软件的查杀,通常不会利用敏感API,特别喜欢混淆与加密,也特别喜欢利用powershell来执行命令,有意思的是大多数,如挂马网页,XML,vbs等等,都喜欢利用powershell来进行恶意代码的执行,powershell安全让人汗颜。
WPS个人版没有宏功能......当初自学的时候确实有点小白,不过后缀.docx并不一定没有宏病毒,好像看过一篇过于APT28。通过嵌入在.docx中的settings.xml.rels组件下载了恶意代码(包含宏组件及vb好像),收集系统信息和运行进程,并使用http,发送到指定的服务器,最后会给大家推荐一些比较好的相关学习的博客与资料。

样本一分析:
 到底宏病毒是什么样?其实就是代码,所以双击是看不到宏病毒的,先Word打开看一看,打开后会提示“安全警告”,启用宏的话就运行了宏,首先你要知道如何去看宏代码Alt + F11就可以弹出,如下图所示:
宏Virus_第1张图片
                     图片一:宏运行界面

你弹出的不是这种界面,而是一个密码框?说明宏被加密了,如下所示:
宏Virus_第2张图片
                     图片二:加密界面

这时候介绍一款工具VBA Password Bypasser,很实用的一款工具,你只需要把.doc在工具中打开,然后Alt+F11再次查看,就会发现密码自动被破解了,进入了图一的界面当中,这样你就见了所谓的宏代码。
宏Virus_第3张图片
                     图片三:VBA Password Bypasser

如何静态提取宏的源码呢?不运行文档能不能提取其中的宏代码?当然也可以,有一款开源的Python的插件,叫做oledump.py,链接:https://github.com/decalage2/oledump-contrib

如图一,发现打开后代码是混淆或加密,代码中根本就看不懂,只能看到几个关键字,这样得话给分析这个宏行为(代码行为)就带来一些干扰,但是不论怎样混淆,代码自身必须是能运行的,也就是必须能运行时识别,运行的方式有很多种,如Shell,Application.Run等关键函数,定位关键字,解密数据,如下所示:
补充:

Function XXX()
            这种格式表示一个函数
End Function

Sub AutoOpen() 
            运行文档时候,自动运行宏
End Sub

宏Virus_第4张图片
                     图片四:入口点
有点OEP的感觉,不过真的就是从这个地方开始,因为使用AutoOpen(),运行文档时候,调用宏代码,如何下断点与动态调试呢?如下所示:
宏Virus_第5张图片
                     图片五:动态调试

这样必要的时候可以动态调试以精准获取数据,下面就来分析KlsJVlijz函数,如下所示:
宏Virus_第6张图片
                     图片六:KlsJVlijz


 利用关键字定位的手段,快速找到关键代码数据,如Shell关键字,Ctrl+F弹出搜索窗口,如下所示:
宏Virus_第7张图片
                     图片七:Shell关键字

 发现Shell后面的数据都是加密过的数据,把上面很多变量都使用+进行了拼接,这时候就要修改一下vb代码,来获取shell到底做了什么操作,如下所示:
宏Virus_第8张图片
                     图片八:修改源码

 如上述代码所示,这样就可以把指令拼接到变量Contenu中,然后保存到文件里,便于我们分析,或者简单点,动态调试直接监视其中观察Contenu的数据粘贴复制出来也是没问题的。
 打开Data.txt看一看,被定向后的内容到底是什么?稍微排版了一下,如下所示:
宏Virus_第9张图片
                     图片九:数据

 这时候就看到了一些关键字如Cmd /V /c Set等,说明要执行一条与cmd相关的指令,且发现拼接起来的变量十powershell,如下所示:
宏Virus_第10张图片
                     图片十:powershell
请看参数powershell的参数是-e ,加密数据你怎么知道是如何加密呢?字符串最后有符号=,像是Base64的特征,而-e是什么?接收Base64编码字符串命令,既然能接收Base64指令,应该没问题,对不对呢?尝试后就知道了,在上一篇博客Xbash中也对powershell的加密做了分析与介绍,解密后如下:
宏Virus_第11张图片
宏Virus_第12张图片
宏Virus_第13张图片
                     图片十一:Base64解密后数据
 是不是解密出错了?为什么解密后还是那么多不认识的数据,而且看上去有点中间竟然还有加密,遇到这种情况一定仔细观察分析,不是靠猜就可以,计算机执行数据一定是由依据的,如下所示:
宏Virus
                     图片十二:数据规则

 这样就明白了,计算器在执行的这条指令的时候就是按照规则去分割,取正确的数据执行,也就是把Y&{m:}等乱七八糟的数据去掉,这些混淆的数据去掉之后应该就没问题了,如下所示:
宏Virus_第14张图片
                     图片十三:还原数据

那么如何编写取混淆的代码呢?压缩包中有别人写的Python写的脚本去混淆代码,10来多行
 个人用的C++写的,第一个版本去混淆代码量与Python差不多,但是规范整理后,写了些注释,而且考虑了很多情况,代码量就上去了,单纯针对去写应该很简单的,C++代码如下:

#include "pch.h"
#include 
#include 
#include 
#include  

using namespace std;

/*
    转换Ascii码
*/

// 保存转换后的数据
vector vNewData;

// 保存获取数字
string nSaveStr;

void FilterNumberOfAscii(const char* str,int & nSize);

int ToAsciiOfchar(int temp);

int main()
{
    // 需要转换的数据
    char Data[] = { "xxxxxxxxx" };

    // 注意不能sizeof求数组大小,会把最后的'\0'也当作数据来比对的
    int nDataSize = strlen(Data);

    FilterNumberOfAscii(Data, nDataSize);

    // 遍历输出转换后的数据
    for (auto iter = vNewData.begin(); iter != vNewData.end(); ++iter)
    {
        cout << (*iter);
    }

    putchar('\n');

    system("pause");

    return 0;
}

// Ascii判断及转换
int ToAsciiOfchar(int temp)
{
    // 保存转换后的Ascii
    char cSaveAscii;

    istringstream is(nSaveStr.c_str());

    is >> temp;

    // 2. 将数字判断是否是ascii码
    if (isascii(temp))
    {
        // 3. 转换成ascii码保存到
        cSaveAscii = toascii(temp);

        vNewData.push_back(cSaveAscii);

        nSaveStr.clear();

        return 1;
    }

    return 0;
}

// 条件过滤
void FilterNumberOfAscii(const char* str, int & nSize)
{
    int temp;

    nSaveStr.clear();

    for (int i = 0; i < nSize; i++)
    {
        temp = 0;
        /*36!78asd99*/
        // 1. 连续的数字就有可能是Ascii
        if (('0' <= str[i]) && ('9' >= str[i]))
            nSaveStr += str[i];
        else
        {
            if (nSaveStr.size() != 0)
                if (!ToAsciiOfchar(temp))
                {
                    // 如果是连续数字且不是Ascii码怎么办?解决思路如下:
                        // 1、注意顺序是最后一个在最前面,调用reverse 反转
                        //  nSaveStr.reserve();
                        //
                        //  for (int i = 0; i < nSaveStr.size(); ++i)
                        //  {
                        //      // 2、把数据入栈即可
                        //      vNewData.push_back();
                        //  }
                    // 因为该样本加密规律 所以上面就用不到了
                    cout << "发现了连续数字非Ascii码: " << temp << endl;
                }

            // vNewData.push_back(str[i]);
        }
    }

    //  注意:因为按照strlen长度来for,最后一次容器中内容也要转换
    if (nSaveStr.size() != 0)
        ToAsciiOfchar(temp);
}

 样本一的分析就到这结束,通过上面的分析应该对宏有一定的认知与解密去混淆的思路,下面一起来看第二个样本。


样本二分析:
 样本二文件名是Invoice_receipt(发票收据),因为这些脚本病毒他们不能随便改图标来达到以假乱真,有时候通过文件名来伪装自己,一不小心就上当了,还是老样子,静态提取或者动态调试根据个人而定,如下所示:
宏Virus_第15张图片
                     图片十四:样本二

 看上去要比上一个复杂多了,那么多文件,但是我们还是要关键点分析,不要看函数是多是少,最终会被执行的数据才是真正的恶意代码,所以找关键字,如下所示:
宏Virus_第16张图片
                     图片十五:Application.Run

 发现执行且调用了函数rGNvwJqoJ,点开其余的文件发现该函数实现在CTrjsVrF,这就好办了,如下所示:
宏Virus_第17张图片
                     图片十六:跟踪数据
补充:

On Error Resume Next 异常处理,如果出错了继续执行不中断
Dim 声明变量并分配存储空间
Set 将对象引用赋给变量或属性


同样发现文本CTrjsVrF中全被加密,还是老方法定位关键执行点,看到Shell代码之后是不是很眼熟,Shell后面又个逗号分割开的vbHide符号,表示隐藏,不用拼接到Continue变量中。
 通过样本1中的方法,获取被真正执行的数据。你仔细观察,其他文件的函数都会被上面所加密的数据调用,所以需要确认被执行的数据即可。
如下图所示:
宏Virus_第18张图片
                     图片十七:获取数据

格式与下面的指令一样:
宏Virus_第19张图片
                     图片十八:指令格式

以上代码被识别的数据做了三件事:
  1、%ComSpEc% C:\Windows\system32\cmd.exe
  2、cmd参数 /c的含义是执行指定字符串然后中止
  3、混淆powershellb变量


最初分析的时候由于自学,耗费了很长一段时间,原因很简单,我没看到iex,也没太注意最后的数据,而分析的思路如下:
 1、分析解密
   Runtime.interOpservices.marSHAl
   ptrostringingUnI([runtime.interOpservices.marshaL]
   securestring to globalaloc unicode
   covertto-securestring -ke (3..34)
 2、确认加密方式convertto-securestring
 3、微软官方关于convertto-securestrin介绍:https://docs.microsoft.com/zh-CN/previous-versions//dd347656(v=technet.10)
 4、解密开始

然后根据微软的参考写脚本解密,结果一直不尽人意,最后又完整分析,才明白字符串 ( $ENV:coMSpeC[4,15,25]-joiN'')的含义,这个是什么?如下所示:
宏Virus_第20张图片
                     图片十九:IEX混淆

 Xbash中也介绍过IEX了,既然是IEX就好办了,直接删除掉这一段代码就行了,然后重定向到文件或者贴上删除后的代码运行,即可还原出源代码,如下所示:
宏Virus_第21张图片
                     图片二十:数据还原
上述代码中已经做了详细的注释,发现解密后的宏与样本一套路是一样的,不过样本二解密后的样本字符串处理的也很好,所有字符串都进行了拆分处理,解密思路如下所示:
宏Virus_第22张图片
                     图片二十一:思路
文章中的宏病毒并不难,并没有涉及太多的数据与API的使用,只是增加了分析的难度与干扰。比如APT宏利用漏洞释放大量的代码,且对屏幕按键进行捕获,利用释放的程序进行网络传输,那些分析起来应该就会困难很多。

链接:https://pan.baidu.com/s/1tI4JFnKVz81zf05rzMgwKg
提取码:dezj
推荐:关于复合文档的研究:https://www.cnblogs.com/AspDotNetMVC/p/3810839.html
推荐:关于宏专辑:https://bbs.ichunqiu.com/forum.php?mod=collection&action=view&ctid=133