用户
搜索
  • TA的每日心情
    慵懒
    2019-4-1 17:55
  • 签到天数: 286 天

    连续签到: 1 天

    [LV.8]以坛为家I

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    31

    主题

    134

    帖子

    5609

    魔法币
    收听
    3
    粉丝
    6
    注册时间
    2015-11-20

    i春秋签约作者春秋文阁

    发表于 2017-12-9 09:52:54 1261786
    本文原创作者:icq5f7a075d/elfbin,本文属i春秋原创奖励计划,未经许可禁止转载
    “病毒,任何时候都要剿!不剿不行,你们想想,你带着老婆出了城,打着BOSS还唱着歌,突然就中了病毒关了机……所以,没有病毒的日子才是好日子!”
    “剿病毒!抢钱粮!扒衣服!睡女人!”
    timg.jpg


    一、简介
    MD5:2908715eec754aba1ad21414b23cafb6
    VT First Submission:2017-12-04 13:34:40
    图标:
    图片1.png
    主要行为:
    ①加密文件,加密后文件后缀名为 ..doc  :
    图片2.png
    ②进行勒索 :
    图片3.png



    二、病毒流程

    图片4.png
    三、邮件传播
    病毒通过邮件进行传播,邮件中包含一个附件。附件中是一个vbs文件。
    图片5.png       图片6.png


    四、vbs分析
    使用记事本软件打开vbs文件,查看源码。vbs代码经过了简单的混淆,代码中使用到的字符串都存放在变量Kullopin中
    图片7.png
    使用wscript  /x 命令动态调试vbs文件
    图片8.png
    解混淆:
    图片9.png
    从这堆字符串中就能猜测这段vbs代码的功能,首先创建XMLHTTP.Adodb.stream对象,利用Get请求,从远程站点(http)读取数据(responseBody),savetofile保存文件到%Temp%路径下,保存的文件名应该是lGGtcrugME.exe,利用Wscript.shell运行exe文件。
    事实果然如此。从如下站点获取数据,生成exe文件:
    图片10.png
    Wscript.shell执行cmd命令,cmd启动lGGtcrugME.exe:
    图片11.png
    图片12.png
    五、lGGtcrugME.exe分析
    该勒索病毒的主要行为如下图所示:
    图片13.png
    接下来,笔者详细叙述分析过程。
    5.1 定位Main函数
    分析lGGtcrugME.exe的第一步是了解lGGtcrugME.exe的静态信息,可以使用Exeinfo .exe工具,了解PE文件编译器(\壳)、区段、入口地址等信息。如图所示,lGGtcrugME.exe极有可能是一款VC++编写的程序:
    图片14.png
    接着,使用IDA进行反编译。但是,IDA未能分析出到Main函数,只能定位到OEP(如下图所示),需要手动地位Main().
    图片15.png
    因为是VC++程序,手动定位Main比较简单。先定位GetCommandLineA,然后继续往下找函数,可以定位到sub_401100,这就是主函数:
    图片16.png
    5.2 生成shellcode
    sub_401100中申请了一段大小0x8668的堆内存,并将dword_416004处0x8668大小的数据复制到该内存中:
    图片17.png
    这0x8668大小的数据是一段shellcode,但是被加密了,调用sub_4016C0进行解密:
    图片18.png
    sub_4016C0中调用sub_4015A0进行解密,每次解密8字节。
    图片19.png
    sub_4015A0里的算法实际上TEA加密算法,笔者利用python还原其算法:
    [Python] 纯文本查看 复制代码
    import types
    def Int(a):
        return int(a&0xFFFFFFFF)
    def Tea(a,b):
        array=[Int(a),Int(b)]
        addr=[Int(0x8DDB12D),Int(0xE378E132),Int(0xC831E7B),Int(0x28B2C399)]
        magic_num=0xC6EF3720
        magic_num2=0x61C88647  #i春秋防盗版文字水印
        for i in range(0,0x20):
            array[1]=Int(array[1]-(Int(addr[1] + Int(array[0] >> 5))^Int(Int(magic_num + array[0])^ Int(addr[0] + Int(0x10 * array[0])))))
            array[0]=Int(array[0]-(Int(addr[3] + Int(array[1] >> 5))^Int(Int(magic_num + array[1])^ Int(addr[2] + Int(0x10 * array[1])))))
            magic_num =Int(magic_num+magic_num2)
        print '%x,%x'%(array[0],array[1])
    return array[0],array[1]
    
    接着,程序调用VirtualProctect将shellcode所在内存页属性修改为可读可写可执行,随后执行这段sehllcode:
    图片20.png
    为了能在IDA中分析这段代码,我们使用OD把这段shellcode dump下来。
    5.3 shellcode分析
    shellcode首先会导入DLL加载API,然后释放PE数据,执行PE代码。下面是详细分析。
    5.3.1获取API地址
    shellcode无法直接调用API,必须先装载动态库,获取API地址才能调用API。动态加载DLL,一般使用LoadLibrary函数,获取API的地址一般使用GetProcAddress函数,只要有了这两个API,就可以调用任何动态库的API。但是在使用这两个API之前,需要先获取这两个API的地址。这两个API都在Kernel32.dll中,而Kernel32.dll已经被加载进内存,所以只要能定位到Kernel32.dll就能调用任意API。
    ①定位Kernel32.dl
    shellcode通过遍历程序已经加载模块,定位kernel32.dll模块:
    图片21.png
    原理:参考《逆向工程核心原理》
        fs:[30]:PEB起始地址
       PEB偏移0x0C:PEB.Ldr指向_PEB_lDR_DATA结构体
         
    [C++] 纯文本查看 复制代码
     typedefstruct _PEB_LDR_DATA[/font][/align][font=楷体, 楷体_GB2312]      {
          ULONG Length; // +0x00
          BOOLEAN Initialized; // +0x04
          PVOID SsHandle; // +0x08
          LIST_ENTRY InLoadOrderModuleList; // +0x0c
          LIST_ENTRY InMemoryOrderModuleList; // +0x14
          LIST_ENTRY InInitializationOrderModuleList;// +0x1c
          } PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24
       通过_PEB_LDR_DATA的InLoadOrderModuleList成员获取_LIST_ENTRY结构
    [C++] 纯文本查看 复制代码
     typedef struct _LIST_ENTRY {
          struct _LIST_ENTRY  *Flink;
          struct _LIST_ENTRY  *Blink;
          } LIST_ENTRY, *PLIST_ENTRY;
    _LIST_ENTRY结构体提供了双向链表机制。链表中保存着_LDR_DATA_TABLE_ENTR结构体信息。_LDR_DATA_TABLE_ENTR
          
    [C++] 纯文本查看 复制代码
    typedef struct _LDR_DATA_TABLE_ENTRY{[/font][/align][font=楷体, 楷体_GB2312]      PVOID Reserved1[2];
          LIST_ENTRY InMemoryOrderLinks;
          PVOID Reserved2[2];
          PVOID DllBase;
          PVOID EntryPoint;
          PVOID Reserved3;
          Unicode_STRING FullDllName;
          BYTE Reserved4[8]
          PVOID Reserved5[3];
          union{
          ULONG CheckSum;
          PVOID Reserved6;
          };
          ULONG TimeDateStamp;
          } LDR_DATA_TABLE_ENTR, *PLDR_DATA_TABLE_ENTRY;
    判断_LDR_DATA_TABLE_ENTR是不是空,如果不是空,则取FullDllName成员,判断字符串是不是“Kernell32.dll”,如果不是则利用InMemoryOrderLinks成员,找下一个结构体,如果是则成功定位到Kernell32.dll。

    ② 获取GetProcAddress地址
    在①中已经获取到kernel32加载的基址,现在获取GetProcAddress地址:
    图片22.png
    原理:详细内容参考PE结构
    加载基址-&gtE头->导出表地址->导出函数序号表和导出函数名表->获取“GetProcAddress”函数序号->获取导出函数地址
    ③获取LoadLibrary地址
    有了GetProcAddress,就很容易获取LoadLibrary地址:
    图片23.png
    至此已经获得LoadLibrary和GetProcAddress这两个API的地址了,就可以加载任何动态库,调用任何API了。
    ④加载其他动态库和API:
    图片24.png
    5.3.2装载PE数据
    获取到自己想用的API后,shellcode就对一段0xD800大小的数据进行解密(笔者没有分析解密算法),解密后的数据是一个PE文件,可以直接DUMP下来分析。
    随后,shellcode将0x400000处0xE600大小的空间清零,将PE文件数据拷贝进去。为什么0xD800大小的数据要拷贝到0xE600大小的空间?因为解密后得到的0xD800大小的数据是以文件对齐存放在内存堆中,而拷贝到0x400000的目的是为了执行这段PE,需要以内存对齐的方式存在。所以,这里发生了从文件对齐到内存对齐的转变。
    既然PE数据拷贝后要执行,执行就要考虑调用API的问题,是不是还是像前文shellcode那样获取每一个API的地址?是又不是\(0^◇^0)/,因为的确要获取每一个API的地址,但获取方法和shellcode中的不同,PE文件中有完整的导入函数表(IAT),这里只需要修复IAT就可以了。
    从解密获取PE文,到将PE文件装载进内存,整个过程PE数据没有在磁盘落地,可以躲避杀软。
    ①内存对齐:
    图片25.png
    图片26.png
    原理:
    先复制PE头和节表数据,然后在节表中获取VirtualAddress和SizeOfRawData,把对应节复制进内存。
    ②修复IAT
    使用了两层循环,外层循环遍历并加载DLL,内层循环遍历导入函数并获取函数地址,修复IAT。
    外层循环遍历导入的DLL:
    图片27.png
    内层循环遍历导入函数修复IAT:
    图片28.png

    原理:参考输入表结构:
    图片29.png
    导入DLL,使用NAME成员值获取DLL名称,直接使用LoadLibrary导入;file:///C:\Users\14215\AppData\Local\Temp\ksohtml\wps3ED2.tmp.jpg
    修复IAT,使用OriginalFirstThunk定位导入函数名,使用GetProcAddress获取导入函数地址,存入IAT表。
    5.3.3执行PE数据
    接下来jmp eax转到PE的OEP去执行:
    图片30.png
    5.4.PE数据分析
    这个PE程序中使用了复杂的加解密算法,很惭愧笔者能力不高水平有限,这些加解密算法,笔者未能分析出来,只是对这段程序的流程进行了分析。
    5.4.1加入开机启动
    程序首先将自己拷贝到%appdata%目录下:
    图片31.png
    file:///C:\Users\14215\AppData\Local\Temp\ksohtml\wps3ED4.tmp.jpg
    并将自己加入开机启动,在注册表中伪装成BrowserUpdateCheck键:
    图片32.png
    5.4.2 生成密钥文件
    在加解密运算中最重要的是如下这段数据:
    "B231B717113902E9F788C7BD0C7ABABAF9B173A7F6B432076B82CBCB7C8149F3CF2F55A8CBDD772BFB4E0A319AE1ED45EB4AA6C4C6BAC6E11014BDD47D3BDDA0DC19B7F217C8A1B33BCAE7681020436907BEC78F0E47AD285D72B8E5466C83114CC40D44A081A604F05E2D147DFC3AEDD9A7B69D493176EFD7D8B0D264D1A2BFB14FECC1378A8D90547A2F6CA070E90F95FCAA54FA26FA5D63DC84C6C3780D4BB41BE4B608343D72DDE52DE40A2A06D56482454F9DF058E65C3F02CBE1B77289F39EC5BDBC58653A35476A205CD7C75A40D34ECFA56DA0A6433E141F0D9AC60DFBAA21E8AEB5658168253A315F298EDBC7850D3D79BB1E15FEF367F5BD27BF8D"
    先利用这段数据计算出文件名:“AE09C984DF6E74640B3271EADB5DD7C65FDE806235B2CDA478E0EFA9129C09E7”在C:\sers\Public\下创建该文件,然后计算出两段密钥数据,将这两段密钥数据写入C:\sers\Public\AE09C984DF6E74640B3271EADB5DD7C65FDE806235B2CDA478E0EFA9129C09E7:
    图片33.png
    5.4.3生成勒索文件数据
    首先,读取加密的数据,在sub_40A315中进行解密,解密出来的是一段html数据。
    图片34.png
    sub_40A315中使用的解密算法也不是很复杂(=^ ^=)。先生成一个置换表,数据从0x0~0x100,使用“AE09C984DF6E74640B3271EADB5DD7C65FDE806235B2CDA478E0EFA9129C09E7”这段数据进行置换,然后取置换表中数据,经过简单的运算,与密文异或得到明文。

    分析到这程序还没有开始进行文件加密,但距离程序结束运行只剩四个函数了,这四个才是真正执行破坏行为的函数:
    图片35.png
    5.4.4 清理内存
    sub_402354的主要行为:
    遍历进程,获取进程名,将进程名转化为小写,如果进程名中含有”outlook"、"ssms"、"postgre"、"1c""excel"、"word"等字符;就将使用 "taskkill /F /T /PID "命令,将这些进程杀掉,因为这些进程特别耗内存。
    遍历进程原理:
    [C++] 纯文本查看 复制代码
    result=CreateToolhelp32Snapshot(2, 0);//拍摄进程快照
    if ( result != -1 )
      {
    Process32FirstW(result, &v18);
    do{
      //对每一个进程的处理
    }while ( Process32NextW(v4, &v18) );
    5.4.5 文件加密
    sub_409B9C :
    ①首先调用GetLogicalDrives()获取系统中存在的盘符,遍历存在的驱动器,调用GetDriveTypeA获取驱动器类型,如果是如下类型则进行标记:

    2
    DRIVE_REMOVABLE
    该驱动器具有可移动媒体; 例如软盘驱动器,拇指驱动器或闪存卡阅读器。
    3
    DRIVE_FIXED
    驱动器有固定媒体; 例如,硬盘驱动器或闪存驱动器。
    4
    DRIVE_REMOTE
    该驱动器是一个远程(网络)驱动器。


    file:///C:\Users\14215\AppData\Local\Temp\ksohtml\wps3EE8.tmp.jpg
    图片36.png
    ②遍历上面的标记过的驱动器(/磁盘),创建子线程进行文件加密
    图片37.png
    ③sub_40994F中调用 FindFirstFileW和 FindNextFileW遍历文件,
    图片38.png
      首先判断文件名是不是“.”或者“..”,如果是则找下一个文件,如果不是继续判断;
         判断是不是文件夹,
        如果是文件夹,夹判断是不是文件名是不是如下文件夹:
    Windows App.Certification Kit.Windows Defender.ESET.COMODO.Windows NT.Windows Kits.Windows Mail.Windows Media Player.Windows Multimedia Platform.Windows PhoneKits.Windows荘hone Silverlight Kits.Windows Photo Viewer.Windows Portable Devices.Windows Sidebar.WindowsPowerShell.NVIDIA Corporation.Microsoft.NET.Internet Explorer.Kaspersky Lab.McAfee.Avira.spytech software.sysconfig.Avast.Dr.Web.Symantec.Symantec_Client_Security.system volume information.AVG.Microsoft Shared.Common Files.OutlookExpress.Movie Maker.Chrome.Mozilla Firefox.Opera.YandexBrowser.ntldr.Wsus.ProgramData
    这些文件夹用于保证系统正常运行,目的是使受害者还有能力使用已经中毒的电脑支付赎金 -_-!
       如果不是上述文件夹,则进行标记,之后进入再次遍历文件。
       如果不是文件夹则判断文件名是不是如下情况:
           a.文件名后四个字符是不是“..doc”,这说明文件已经被加密
           b.文件名是不是“Read__ME.html”,这是勒索页面文件
           c.文件名是不是“AE09C984DF6E74...”,这是密钥文件
       如果是上述文件则不进行加密,其他文件都会进行加密。sub_409081就是真正的加密过程,具体算法笔者没分析出来o(╥﹏╥)o,猜测是RSA加密。加密后文件尾会追上如下数据,这是解密时需要的数据,和密钥文件中的数据相同。
    图片39.png


    5.4.6删除卷影,日志等
    sub_4096CB 创建一个bat文件:
    图片40.png

    删除卷影,修改远程桌面服务,删减日志记录(wevtutil.exe cl)
    5.4.7 删除原始文件
    sub_40979F:
    图片41.png
    ShellExecuteExW执行的是如下命令:
    Open C:\Windows\system32\cmd.exe /c del C:\Users\Desktop\lGGtcrugMEexe

    后记:
    本文对Globelmposter病毒进行了分析,但是笔者能力不高水平有限,只叹“犹恨未能登绝顶,莲花峰外数天都”,勒索病毒中最精彩的加密部分未能分析清楚,只有抽丝未能剥茧,甚是遗憾。唯有一句“黄沙百战穿金甲,不破楼兰终不还”自勉。


    virus(pw_infected).zip (1.05 MB, 下载次数: 50)

    本帖被以下淘专辑推荐:

    0回复。。。看来这里还是搞web的多,搞逆向的少啊
    使用道具 举报 回复
    发表于 2017-12-11 17:55:06
    减肥的胖子 发表于 2017-12-11 16:40
    0回复。。。看来这里还是搞web的多,搞逆向的少啊

    是啊,楼主表示很难过
    使用道具 举报 回复
    z7788520 i春秋作家 ← 假 真➡i春秋打杂员 突出贡献 积极活跃奖 核心白帽 白帽大神 i春秋签约作者 热心助人奖
    地板
    发表于 2017-12-12 10:59:19
    大佬大佬
    不惹事,不生事,妖魔鬼怪快走开
    使用道具 举报 回复
    发表于 2017-12-12 11:33:34
    大佬怎么学的逆向,我也想学
    http://www.imsunshine.cn/
    使用道具 举报 回复
    发表于 2017-12-12 16:47:50
    Aedoo 发表于 2017-12-12 11:33
    大佬怎么学的逆向,我也想学

    汇编开始
    使用道具 举报 回复
    很强,点赞
    使用道具 举报 回复
    发表于 2017-12-14 14:56:10
    刚开始学习逆向恶意代码,LZ好厉害
    使用道具 举报 回复
    发表于 2018-3-11 23:49:26
    版主请教下,使用OD把这段shellcode dump下来,shellcode是在新申请的内存中,也就是未知的地址,那具体怎么操作dump呢?简单的脱壳dump会,但是申请内存的始终没搞定,具体哪个点,有什么注意的地方?麻烦指点一下,谢谢!
    使用道具 举报 回复
    发表于 2018-3-12 09:51:41
    SuperAC 发表于 2018-3-11 23:49
    版主请教下,使用OD把这段shellcode dump下来,shellcode是在新申请的内存中,也就是未知的地址,那具体怎么 ...

    使用OD动态分析时,我们先在LocalAlloc()处下段,这个API在这里分配了0x8668大小的内存,执行这个API后,eax里存放的就是分配的内存起始地址。接着继续调试,等到执行完解密过程后,我们就可以dump shellcode了。具体过程是在数据窗口选中这0x8668大小的数据,右键-二进制复制,然后在16进制编辑器中(eg:010editor)中复制数据并保存文件,这就将shellcode dump下来了。
    使用道具 举报 回复
    发表于 2018-3-12 10:41:56
    icq5f7a075d 发表于 2018-3-12 09:51
    使用OD动态分析时,我们先在LocalAlloc()处下段,这个API在这里分配了0x8668大小的内存,执行这个API后, ...

    感谢老大,这样就懂了,之前以为需要dump成exe才能解析,直接2进制流IDA也能解析那就OK了,不过sp-analysis failed 这种问题能解决不?不然F5不了哈哈~
    使用道具 举报 回复
    发表于 2018-3-12 10:59:05
    SuperAC 发表于 2018-3-12 10:41
    感谢老大,这样就懂了,之前以为需要dump成exe才能解析,直接2进制流IDA也能解析那就OK了,不过 ...

    导致这个问题的原因是IDA在分析函数时发现栈不平衡,除了详细分析,没有好的解决办法。。。
    使用道具 举报 回复
    发表于 2018-3-12 11:05:06
    icq5f7a075d 发表于 2018-3-12 10:59
    导致这个问题的原因是IDA在分析函数时发现栈不平衡,除了详细分析,没有好的解决办法。。。 ...

    好的~那只好对着OD慢慢调了,谢谢
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册