• ocky勒索软件恶意样本分析2


    locky勒索软件恶意样本分析2

     阿尔法实验室陈峰峰、胡进

    前言

    随着安全知识的普及,公民安全意识普遍提高了,恶意代码传播已经不局限于exe程序了,Locky敲诈者病毒就是其中之一,Locky敲诈者使用js进行传播,js负责下载外壳程序,外壳程序负责保护真正病毒样本,免除查杀。本文主要对Locky外壳程序和核心程序做了一个分析,来一起了解Locky代码自我保护的手段以及核心程序对文件加密勒索过程的分析。

    一        样本基本信息

    Js下载者:f16c46c917fa5012810dc35b17b855bf.js

    外壳:e7c42d7052e59db13d26e3f3777d04af.exe

    核心程序:9DF5F1DA758679672322C2D03DAE77A3.exe

    二       详细分析

    第一节   JS下载者分析

    Js下载者的功能是下载Locky勒索病毒并执行,其使用了代码混淆的方法,使得文件内容无法被识别,而且其变种很多,所以很难对其进行检测

    我们捕捉的原始脚本如下

    1

          [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

     

    经过去除花指令,语法整理,变量重新标明,等技术手段,最终得到的脚本:

    2

    现在看清楚了,其实就是一个下载者,当然我们将下载链接替换掉了。

    JS的代码经过混淆的,所以会出现很多版本,不对其进行还原,是无法判断其是否为恶意

    下面我们来分析一下下载的程序(即外壳程序)

     

    第二节  外壳程序的分析

    下载获得的程序是在核心程序的基础上增加了一个外壳,保护核心程序不被杀毒软件查杀。核心程序被分成4部分加密存放在程序中,外壳程序首先先对核心程序进行解密合并然后在内存中执行。

    外壳程序解密核心程序的代码也被加密,这里我们称之为MainDecode代码

    #01  解密MAINDECODE

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    MainDecode代码被加密了,下面是加密后的代码

    3

    解密函数如下

    4

    由于混淆代码太多,我就不贴汇编代码了,下面是我用C还原后的代码

    5

    看这段代码是不是有点晕,没关系,作者耍了一下

    首先我们要获得解密的长度,病毒作者的计算方式是 -0xe05-lpCodeAddr+(lpCodeAddr+i)

    我们展开看就是-0xe05-lpCodeAddr+lpCodeAddr+i

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    我们给lpCodeAddr去掉就是 i-0xe05

    也就是如果i-0xe05等于0,那么就退出循环,也就是加密代码的长度为e05

    我们再看看解密单个字节的DescryptByte算法

    6

    我们就用代码解密,后的格式如下

    7

    解密完成后程序采用堆栈不平衡形式跳到解密后的代码里

    #02  MAINDECODE拼凑完整的核型程序文件

    本病毒里面藏了一个主要功能的Pe文件(我们称之为核心程序),这个文件不但加密了,并且被分割成四块存放在文件中。

    1.合并核心程序

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    核心程序被分割成四部分保存在文件中,位置大小信息被加密存放,我们对其解密后获得四部分的位置偏移以及大小。

    解密代码

    8

    解密后的数据如下

    9

     

    MainDecode会申请四组的A buffer的总大小得长度,

     

     10

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    再给他们拼凑起来,

     

     11

    总大小为0x11B31,此时Buffer中的数据还是被加密过的,还需要去对这块内存进行解密。

     

    2.解密拼凑后的PE文件

    从文件中提取并组后后的PE文件数据是被加密过的,所以还需要去对这块数据进行解密。解密算法采用了梅森旋转随机数算法产生随机数,通过处理后对数据进行移位,获得最终数据,梅森旋转随机数算法产生的数是随机,但是他给种子写死了,所以产生的数据肯定是一样的,第一次初始化用的key是12BD6AAh,申请一个大A buffer size=0x11b31,用来存放最终数据,B buffer size=0x11b31存在加密后的PE文件,申请一个Int数组C buffer size=0x11b31存放解密表。

    初始化C从0开始一直到0x11b30

    12

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    使用梅森旋转随机数算法产生随机数,初始化用的key是12BD6AAh

    13

    使用梅森旋转随机数算法产生的随机数S,

    C[i]=C[S%0x11b30]

    这样最终生成解密表C,通过解密表C对B Buffer进行解密

    A[i]=B[C[i]]

    获得最终的数据存放在A Buffer中

    14

    现在我们来看看解密后A的内容,不是个PE文件,还得再做一次解压缩的操作

    15-27$IYSZRV2S)6_0ZUNNJO]F

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    内存地址+4 等于解密后的长度

    内存地址+8 等于需要解压缩的长度(0x113b1-0xc=0x11b25)

    16-1

    用COMPRESSION_FORMAT_LZNT1解压缩

    17-GSRW8L3@Z~RXSLNJJZDDJY9

    解压后的结果出现4D5A,PE程序出现了

     

    18-XN`WMZG0QC8``}C`WP~WD~M

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

     

    #03  MAINDECODE执行核心程序

    MainDecode将核心程序拼凑完整并解密后,就开始调用核心程序。调用方式是在内存中加载执行。

    1.        拷贝PE头

    19-1

    2.        拷贝节

    20-PC5NY3171}M6USDSG}4@_EH

    3.        修复重定位表

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    21-`3Q0]QFE(BVS)(L[9JQ`UT8

    4.        修复导入表

    22-BOSGRQ1T{YBV~{LTV7$)}O7

    5.        修复节点属性

    23-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    6.        修复SEH链

    24-ZF5QTTTO@3G_Q~L){18)53C

    7.        填写寄存器

    25-S@ET$[56}JVPZ$69L~P~`F4

    8.        修复堆栈

    26-LFP{H[X3}S{2PKK`ME2SX@3

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    9.        跳到新OEP处

    27-]RI4LSRO8}Q[$YY65UUY194

    至此,外壳程序完成使命,核心程序开始工作。

     

    Ps:快速提取核心程序过程

    程序载入后运行后发现开始位置处的代码被写成00,对写入00位置的地方下断点。运行

    28-DF]TNR2R[7OW4J9WQ7_N]{U

    Ctrl+F9

    29-611MXYE2XIN`2K5`E}BBHHF

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    继续单步

    30-1

    31-OCWK@)UQT5O2I44$}[XO[~O

    就可以dump核心程序代码主体。

     

    第三节核心程序分析

    #01 恶意代码流程分析

     

    1 获取系统语言,判断是否是俄罗斯的,是的话直接退出,不是继续下面步骤。

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    32-R@~MD$WJM~}7[AN1CPF[F7

    2 获取系统信息,并计算出id(区别不同客户端用)

    33-U)N$D`6[F{DEX${N(F03]@W

    取其中的{..}内容获取md5

    34-AKI1M8YX~}`Y0B]8PF~$W47

    35-R2SH9}IT{CZ6MPO{P$K(2@6

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    前16 bytes就是id

    3 根据id算出一个项,并写入HKEY_CURRENT_USERSoftwarefA21OkqPsY。

    并且计算出字符串,找注册表中是否有RSA 的 key。有key就直接进行加密线程。没有就去网络获取RSA key。

    36-1

     

    37-SLH0UHKN1[ZTXQVM7R5(56H

    4 获取用来加密的主要RSA key

    根据系统信息组成下面字符

    38-D~@`OE}PIWMHT84)TOKN4)P

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

     

    求md5值,buffer = md5(buffer) + buffer

    39-MMYPD~]C}~XELP6W7)FXK66

     

    加密

    40-Y{(V`CKN_K74XH5KFU_M8O9

    41-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    加密完数据后,首先会向固定的ip发送来获取key

    42-XON`B7XT$HG(5EKVW32XSCT

    随机取其中一个,post上面加密的信息,当所有ip都获取不到时候,会使用一个随机算法获得一个域名,然后请求来获取key。

    HTTP/1.1 POST 错误!超链接引用无效。

    43-`8AL79}1Y9}T$0~J0K39G~F

     

    44-5_$ME)PWF]IIJC_[0(_1$$P

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    随机生成域名用到的最后一段(以.ru, .info, .biz, .clicksu, .work, .pl, .org, .pw, .xyz结尾的域名)

    45-1

    发送请求,得到返回值后需要解密。

    46-7J]$}8`AFL)_C5J_7FUNCVB

    47-1

     

    48-YYBQ$FDK}5C4A~3N`EH_GVG

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    解密后验证返回数据解密后是否合法

    49-1

    RSA key 格式

    50-1

     

    最后加密写入注册表。

    5 获取勒索文本

    类似上面获取key的情况,发送的数据变成id= EA3CF08A962DF289 &act=gettext&lang=zh

    也是先加密,在POST,回来的数据先解密,再验证MD5,最后加密写入注册表。

    6 判断硬盘类型,并执行相应操作

    51-`B~~9W)$S`0JS@92PXMP026

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    7 创建线程,查找待加密的文件,并加密文件!(主要加密线程)

    52-1

    查找目录或文件名中包含下面字段就跳过

    53-1

     

    找后缀名是下列后缀名的文件

    54-Y]0S(8{HQ{82_P)[M{)K8S4

    8 找到文件后对文件进行加密

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    文件加密主要随机生成16 bytes的aes_key,用RSA加密aes_key,再用AES 加密文件名,和文件主体。详细过程查看03。每次加密一个文件后,会在该目录写入勒索信息(_HELP_instructions.txt)!。

    9 所有文件都加密完成后,会向C&C发送加密的状态。

    向服务器POST过程与获取KEY以及TEXT一致,主要发送的数据如下:

    55-1

    10 删除备份

    56-1

    11写入开机启动

     

    57-3F@H9(9]TVG3{0E(]}@K$ZK

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    58-G]VUAJX]N4M{SML`9T]I~ZG

    59-CH`JDGFVZ5Y5(V~_6JW`NZ1

    12 加密完成后还会写入注册表,表示加密完成。

    60-_]WN@C9@AVXGVEAMPATL5X9

    最终的注册表如下

    61-YV2OBMUYRBE8A(62GD{{R_P

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    13 最后修改桌面以及显示勒索界面

    修改桌面背景

    62-1

    63-R1F9@33RHXHX20@NXJ5G6G6

    64-Q3G34P6SZ]`$0C)W}8X{MBH

    65-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    打开勒索信息文件

    67-W4R~BJO($6UZ707Z8(RDHWF

    14 移动自身到tmp目录下,并改名

     

    68-WWIDYI5PB(Z0W3L~ZZ4AITB

    69-1

    加密完成后删除当前目录下文件

    70-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    #02文件加密过程

    1 首先根据文件路径以及名称获取文件的属性。

    71-1

    2 随机生成0×10大小的字符,用来组成新的文件名

    72-1

    新的文件名 = id + Random_16_char + .locky

    3直接将旧的文件重命名为新的文件名

    73-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    4 随机生成16 bytes 的AES_key

    74-1

    5 RSA加密16 bytes AES_Key,生成0×100 bytes 密文

    75-1

    加密使用的PKCS1先将明文扩充为长度为0×100大小的数据

    EM = 0×00 || 0×02 || PS || 0×00 || M.

    00 02 + random(0×100 – 3 – len) + 00 + M

    明文扩展后加密。扩展的格式如下。

    76-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    由于PS是系统生成的,自己加密的时候不会和程序加密的RSA有一样的结果!

    6 aes加密文件名

    77-1

     

    78-1

    79-1

    80-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    81-1

    加密的过程:

    首先生成0×800大小的数据

    82-1

    83-1

    84-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    7 读取文件,并加密

    加密过程同加密文件名的时候,但是中间数据aes加密后从0×230偏移处开始异或。

    当超过0xff后,会使用下面方式继续增加中间数据

    85-1

    8 将加密结果写入文件

    86-T1

    9 写入文件RSA加密的AES_key(0×100)和AES加密的文件名(0×230)

    87-1

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]  

    10 最后文件的组成

    88-1

    三  总结

    Locky勒索病毒,采用很多对抗检测技术,其JS代码、外壳代码很容易变形能够躲避大部分的检测;其采用的是RSA2048位的密钥加密aes_key,每个客户端收到的key都是唯一的,文件被加密后没有Key的情况不能解密。这是一个非常难检测、难防范,危害性极大的病毒

            [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30] 

  • 相关阅读:
    Android 自定义 ListView 上下拉动刷新最新和加载更多
    Android Activity 及其子类
    MySQL INSERT插入条件判断:如果不存在则插入
    MySQL中函数CONCAT及GROUP_CONCAT
    数据权限的设计与实现
    shiro过滤器过滤属性含义
    shiro注解
    python中,花括号,中括号,小括号的区别
    SpringBoot项目eclipse运行正常maven install打包启动后报错ClassNotFoundException
    Nginx对某个目录或整个网站进行登录认证的方法
  • 原文地址:https://www.cnblogs.com/xdans/p/5412870.html
Copyright © 2020-2023  润新知