当前位置: 首页 > news >正文

勒索病毒LockBit2.0 数据库(mysql与sqlsever)解锁恢复思路分享

0.前言

今天公司服务器中招LockBit2.0勒索病毒,损失惨重,全体加班了一天基本解决了部分问题,首先是丢失的文件数据就没法恢复了,这一块没有理睬,主要恢复的是两个数据库,一个是16GB大小的SQLserver数据库,另一个是一个15GB大小的Mysql数据库,最后丢失了1%左右的内容顺利恢复了两个数据库,因为lockbit勒索病毒的分享给文章太少,这里就记录下自己恢复的过程。

1.恢复逻辑

lockbit2.0的锁定逻辑是针对有价值的文件,头部4KB给你加密,尾部4KB给你加密,然后中间隔一段随机(也可能是固定间隔,没有考证过)给你加密大约4KB,全局给你大约加密不到1%的数据,因此恢复的逻辑也就是舍弃这些被加密的数据,尽可能恢复数据库正常。

注意!!!,lockbit2.0不存在找人破解的可能性,找人弄也是按照上面的逻辑,懂程序原理的都知道lockbit的加密是无法被破解的,因此注意各位不要被骗!

另外我们能恢复数据库主要是我们的数据库有以下特点,数据库极大,表内条数极多,这样被损坏的数据才能最大概率着落在表内数据的,并且表内多是流水账数据,丢失部分的损失可控,因此修复的可能性最高,如果你的数据库不大,几百兆,内容不多表结构复杂的,没什么修复的价值。

2.sqlserver恢复

sqlserver市面是存在恢复软件的,我找的是达思科技的软件,1399元买了一天的使用权限,通过被锁的文件和1年前数据库文件(一半大小)一起合并进行了恢复,最后的回复结果是大致回复了99%的数据,部分表格少几条数据,有一个表格是40000多条数据,少了20条,另一个表格18000条数据,少了8条,少的数据就是被加密的数据,被舍掉了。因为sqlsever按照我查的资料,主要被加密的数据一个是头文件,也就是表结构,主要是通过过去的备份文件恢复的,中间的数据就是舍弃。这个恢复成功的思路对我后面回复mysql提供了较大的帮助。

最终sqlsever恢复,但是因为是财务软件的数据库,少的数据还是会导致很多模块计算有问题,这个只能后续慢慢反查和处理了。

注意,一天时间比较宝贵,请提前给自己的机子搭建好本地sql server环境,到恢复成功大约耗时要18个小时,需要通宵干。

3,mysql恢复

mysql恢复是最复杂的,首先mysql市面上是没有成品恢复软件的,不要被骗。因此只能动手恢复mysql的原始文件是分立的文件,每一个文件都被加密了,原始数据库是二进制文件,因此恢复原始数据库文件的难度最大,几乎是不可能被恢复。

因此我们恢复瞄准的是备份的.sql文件,.sql文件是一个类似文本文件,可读性高,恢复起来比较容易。

3.1 mysql文件的逻辑与查看

mysql文件的基本逻辑是如下

建立数据库(一句话)

{

 建立表A

建立表A结构(列)

插入表A的数据 *N

。。。。

}  *N个表循环

其中头部的4KB是建立表头和建立表结构,这一块只能手敲,具体方法是自己建立数据库,复原表结构后导出为sql文件,然后比对着sql文件自己手动恢复头部4KB文件。

第二部分是建立表结构,表结构不复杂的话大约是1KB左右的样子

第三部分是插入表数据,这个是数据库中最大的一块,我们的mysql会将一大堆数据写成一个insert插入,每一个insert的最长是2MB字节的大小,因此如果损坏的4KB落在这里的话,就要排除掉这2MB,后续再维修这2MB。

加密的逻辑是随机加密(待考),因此文件内部被锁的4KB落在insert中的概率最大。

对于这种单文件超大的sql文件,是没有查看工具的,因此首先用c#写一个分析工具,逐行分析这个sql文件是干什么的

使用下面的代码,可以将每一行都打印到log文件中,使用文本打开log文件可以查看sql语句的结构。

static void Main(string[] args){///*          sql有效性验证算法+打印算法int linenum = 0;string filename = "I:\\mysql_backup_20231008__00_00.sql.lockbit";string filename1 = "I:\\log.text";string filename2 = "I:\\xiufuz_error.sql";String line;bool bResult;int[] errorcode = new int[5];try{StreamReader sr = new StreamReader(filename);StreamWriter sw1 = new StreamWriter(filename1);//StreamWriter sw2 = new StreamWriter(filename2);StringBuilder sb = new StringBuilder();     //定义sb.Clear();int isbinary = 0;int errortick = 0;int isrightorerror = 0;while (!sr.EndOfStream){//lstchar2 = lstchar1;//lstchar1 = currentChar;string readlinest = sr.ReadLine();byte[] charArray = Encoding.Default.GetBytes(readlinest);sb.Append(readlinest);if (charArray.Count() > 0){if (charArray[charArray.Count() - 1] == 0x3b){line = sb.ToString();sb.Clear();     //清空linenum++;byte[] bytetemp = Encoding.Default.GetBytes(line);isbinary = 0;errortick = 0;isrightorerror = 0;     //正确for (int i = 0; i < line.Length; i++){if (i + 7 < line.Length){if (line[i] == '_'){try{if (line[i+1]=='b'&&line[i+2]=='i'&&line[i+3]=='n'&&line[i+4]=='a'&&line[i+5]=='r'){isbinary = 1;}}catch{}}}if (isbinary == 0){if (!IsChineseLetter(line, i)){errorcode[errortick] = Char.ConvertToUtf32(line, i);errortick++;if (errortick > 3){if (Char.ConvertToUtf32(line, i) < 0xff00){}isrightorerror = 1; //错误break;}}}else{ }}if (isrightorerror == 1){//错误//sw2.WriteLine(line);sw1.WriteLine("行:" + linenum.ToString("D5") + "错误,内容:" + line.Substring(0, 50) + "---" + errorcode[0].ToString("X4") + ',' + errorcode[1].ToString("X4") + ',' + errorcode[2].ToString("X4"));//Console.WriteLine("错误字符,line:" + linenum.ToString() + ",长度:"+line.Length.ToString()+"---" + errorcode[0].ToString("X4") + ',' + errorcode[1].ToString("X4") + ',' + errorcode[2].ToString("X4"));}else{ //正确//sw1.WriteLine(line);sw1.WriteLine("行:" + linenum.ToString("D5") + "正确,内容:" + line.Substring(0, line.Length > 50 ? 50 : line.Length));}if (linenum % 100 == 0)Console.WriteLine("Line:" + linenum.ToString());}}}sr.Close();sw1.Close();//sw2.Close();Console.WriteLine("Final Line:" + linenum.ToString());}catch (Exception e){Console.WriteLine("Exception: " + e.Message);}finally{Console.WriteLine("Executing finally block.");}while (true) ;
}

通过上述文件打印出来的log可以观察到非常多的信息:

如下图,这是一个正确的建立表格,插入数据的结构,由7行的建立表头,若干行的INSERT数据和1行的解锁组成,按照这个逻辑可以查看每一个表格的语句是否完成,如图我战士的after_sales这个表格就非常完整。

如下图所示,这是一个典型的中间有损失的表,可以看到表结构是正确的,插入语句在21393行出现了错误,这一条语句的数据就要被舍弃,后续再根据重要性选择恢复剩余的细节,但是这个表的绝大多数部分都会恢复并且是好的

3.2 使用程序辅助分离sql文件中的好的部分和坏的部分

这里我使用的C#写的处理程序,处理逻辑是,按照分号加换行符来确认不同的sql语句行,在每一行中逐个字节判断字节是不是有效的文本信息,如果本行文件中出现了三次以上的非Unicode字符的字节,我们就判定这一行被加密了,将这一行复制到错误的文件中等待修复,反之则复制到正确的文件中修复。

附C#代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace 勒索修复程序
{class Program{//我的数据库中各个国家的文字都有,因此这里做了针对性的排除static bool IsChineseLetter(string input, int index){int code = 0;int chfrom = Convert.ToInt32("4e00", 16); //范围(0x4e00~0x9fff)转换成int(chfrom~chend)int chend = Convert.ToInt32("9fff", 16);if (input != ""){code = Char.ConvertToUtf32(input, index); //获得字符串input中指定索引index处字符unicode编码if (code < 0x80)        //英文是正确的return true;if (code >= chfrom && code <= chend){return true; //当code在中文范围内返回true}if (code >= 0xa0 && code <= 0xff){return true; //当code在中文范围内返回true}if (code >= 0xff00 && code <= 0xffee){return true; //当code在中文范围内返回true}if (code >= 0x3000 && code <= 0x30ff){return true; //当code在中文范围内返回true}if (code >= 0x3300 && code <= 0x36ff){return true; //当code在中文范围内返回true}if (code >= 0x13a0 && code <= 0x13ff){return true; //当code在中文范围内返回true}if (code >= 0x2000 && code <= 0x22ff){return true; //当code在中文范围内返回true}if (code >= 0x2400 && code <= 0x27ff){return true; //当code在中文范围内返回true}if (code >= 0x0100 && code <= 0x02ff){return true; //当code在中文范围内返回true}if (code >= 0x0370 && code <= 0x03ff){return true; //当code在中文范围内返回true}if (code >= 0x0400 && code <= 0x05ff){return true; //当code在中文范围内返回true}if (code >= 0x0600 && code <= 0x06ff){return true; //当code在中文范围内返回true}if (code >= 0x0e00 && code <= 0x0fda){return true; //当code在中文范围内返回true}if (code >= 0xac00 && code <= 0xd7a3)       //韩文{return true; //当code在中文范围内返回true}if (code >= 0xaa80 && code <= 0xaac2)       {return true; //当code在中文范围内返回true}if (code >= 0x1d00 && code <= 0x1eff){return true; //当code在中文范围内返回true}if (code >= 0x0900 && code <= 0x0bff){return true; //当code在中文范围内返回true}if (code >= 0x3400 && code <= 0x4dbf)       //繁体{return true; //当code在中文范围内返回true}else{return false; //当code不在中文范围内返回false}}return false;}static void Main(string[] args){///*          sql有效性验证算法int linenum = 0;string filename = "I:\\mysql_backup_20231008__00_00.sql.lockbit";    //目标文件string filename1 = "I:\\xiufuz_right.sql";    //正确的保存文件string filename2 = "I:\\xiufuz_error.sql";    //错误的保存文件String line;bool bResult;try{StreamReader sr = new StreamReader(filename);StreamWriter sw1 = new StreamWriter(filename1);StreamWriter sw2 = new StreamWriter(filename2);StringBuilder sb = new StringBuilder();     //定义sb.Clear();int isbinary = 0;int errortick = 0;int isrightorerror = 0;while (!sr.EndOfStream){//lstchar2 = lstchar1;//lstchar1 = currentChar;string readlinest = sr.ReadLine();byte[] charArray = Encoding.Default.GetBytes(readlinest);sb.Append(readlinest);if (charArray.Count() > 0){if (charArray[charArray.Count() - 1] == 0x3b){line = sb.ToString();sb.Clear();     //清空linenum++;byte[] bytetemp = Encoding.Default.GetBytes(line);isbinary = 0;errortick = 0;isrightorerror = 0;     //正确for (int i = 0; i < line.Length; i++){if (i + 7 < line.Length){if (line[i] == '_'){try{if (line[i+1]=='b'&&line[i+2]=='i'&&line[i+3]=='n'&&line[i+4]=='a'&&line[i+5]=='r'){isbinary = 1;}}catch{}}}if (isbinary == 0){if (!IsChineseLetter(line, i)){errorcode[errortick] = Char.ConvertToUtf32(line, i);errortick++;if (errortick > 3){Console.WriteLine("错误字符,line:" + linenum.ToString() + ",长度:"+line.Length.ToString()+"---" + errorcode[0].ToString("X4") + ',' + errorcode[1].ToString("X4") + ',' + errorcode[2].ToString("X4"));if (Char.ConvertToUtf32(line, i) < 0xff00){}isrightorerror = 1; //错误break;}}}else{ }}if (isrightorerror == 1){//错误sw2.WriteLine(line);}else{ //正确sw1.WriteLine(line);}}}}sr.Close();sw1.Close();sw2.Close();Console.WriteLine("Final Line:" + linenum.ToString());}catch (Exception e){Console.WriteLine("Exception: " + e.Message);}finally{Console.WriteLine("Executing finally block.");}while (true) ;// */while(true);}}
}

执行效果如下图

可以看到,被攻击的段落中至少出现三个FFFD以上的,这个就是被攻击的段落的特点,将没被攻击的干净的sql文件导出后进行下一步处理。

3.3 mysql文件导入库

mysql文件导入mysql库的过程需要自己执行,导入过程中发现错误随时进行处理和补全表结构,这里因为表是你自己建的,别人也帮不了你,是一个苦功夫,但是我的数据库16GB大小,表结构占位不到1MB,因此表结构被加密的概率较低,实际恢复过程中我的数据库就一个表结构被损坏了,因此就补了一个表结构就完成了数据库的恢复。

3.4 其他被损坏的文件的处理

首先我通过C#将500MB左右的损坏文件拆成了10个50MB的文件,按照每个文件50行

附代码

static void Main(string[] args){///*     //拆分算法try{string line="";int linenum=0;byte lstchar1=0, lstchar2=0;byte currentChar=0;int block = 0;string filename = "I:\\xiufuz_error.sql";string filename1 = "I:\\error1.sql";string filename2 = "I:\\error2.sql";string filename3 = "I:\\error3.sql";string filename4 = "I:\\error4.sql";string filename5 = "I:\\error5.sql";string filename6 = "I:\\error6.sql";string filename7 = "I:\\error7.sql";string filename8 = "I:\\error8.sql";string filename9 = "I:\\error9.sql";string filename10 = "I:\\error10.sql";StreamReader sr = new StreamReader(filename);StreamWriter sw1 = new StreamWriter(filename1);StreamWriter sw2 = new StreamWriter(filename2);StreamWriter sw3 = new StreamWriter(filename3);StreamWriter sw4 = new StreamWriter(filename4);StreamWriter sw5 = new StreamWriter(filename5);StreamWriter sw6 = new StreamWriter(filename6);StreamWriter sw7 = new StreamWriter(filename7);StreamWriter sw8 = new StreamWriter(filename8);StreamWriter sw9 = new StreamWriter(filename9);StreamWriter sw10 = new StreamWriter(filename10);//Read the first line of textStringBuilder sb = new StringBuilder();     //定义sb.Clear();while (!sr.EndOfStream){//lstchar2 = lstchar1;//lstchar1 = currentChar;string readlinest = sr.ReadLine();byte[] charArray = Encoding.Default.GetBytes(readlinest);sb.Append(readlinest);if (charArray.Count() > 0){if (charArray[charArray.Count() - 1] == 0x3b){line = sb.ToString();sb.Clear();     //清空linenum++;if (linenum % 100 == 0)Console.WriteLine("Line:" + linenum.ToString());//清空前处理if (block == 0)sw1.WriteLine(line);else if (block == 1)sw2.WriteLine(line);else if (block == 2)sw3.WriteLine(line);else if (block == 3)sw4.WriteLine(line);else if (block == 4)sw5.WriteLine(line);else if (block == 5)sw6.WriteLine(line);else if (block == 6)sw7.WriteLine(line);else if (block == 7)sw8.WriteLine(line);else if (block == 8)sw9.WriteLine(line);else if (block == 9)sw10.WriteLine(line);if (linenum > 50){if (block < 9){linenum = 0;block++;}}} }}sw1.Close();sw2.Close();sw3.Close();sw4.Close();sw5.Close();sw6.Close();sw7.Close();sw8.Close();sw9.Close();sw10.Close();Console.WriteLine("Final Line:" + linenum.ToString());while(true);}catch (Exception e){Console.WriteLine("Exception: " + e.Message);}finally{Console.WriteLine("Executing finally block.");}// */while(true);}

拆成50MB左右的文件后就可以使用notepad打开了,打开后根据每一条的重要程度选择性恢复,这里的回复只能自己手动回复的,具体的办法是将里面损坏的部分删除后,手动对齐格式,把剩余能插入的数据再执行插入回数据库中。是一个手工的水磨细活。

4 总结

全部恢复耗时两个人24小时通宵解决了,尽可能的减少了勒索病毒对公司的影响。

勒索病毒是对公司的损失是重大的,本次勒索病毒我们自己总结如下经验

1,首先杀毒系统要装,有钱就买个卡巴斯基装上(淘宝有便宜的),没钱装一个360,一定要有杀软

2,内网别偷懒,该划分vlan就划分vlan,至少能做好数据隔离

3,核心数据库资产一定要做一个云端备份,可以用阿里云或者腾讯云的付费产品,不要迷信本地备份,我们就是本地备份顺着共享文件夹全部一锅端了,三个备份机制全部失效。

4,如果真的依靠本地备份,就在vm exsi中做镜像级别的备份,这个备份不会被干掉,反之所有的windows级别的备份都会被勒索病毒干掉。

http://www.lryc.cn/news/195672.html

相关文章:

  • 超简单小白攻略:如何利用黑群晖虚拟机和内网穿透实现公网访问
  • Ubuntu 16.04 LTS third maintenance update release
  • Java学习_day01_hello java
  • UnitTesting 单元测试
  • C++内存管理:其五、指针类型转换与嵌入式指针
  • 常见锁的分类
  • vue 鼠标划入划出多传一个参数
  • svn项目同步到gitLab
  • 图解Dubbo,Dubbo 服务治理详解
  • Css 如何取消a链接点击时的背景颜色
  • 1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计
  • ABAP 新增PO计划行时 新增行交货日期默认当前最大交期
  • VSCode怎么创建Java项目
  • 软件工程与计算(十四)详细设计中面向对象方法下的模块化
  • 商城免费搭建之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c
  • python教程:selenium WebDriver 中的几种等待
  • 【MATLAB源码-第49期】基于蚁群算法(ACO)算法的栅格路径规划,输出最佳路径图和算法收敛曲线图。
  • LabVIEW生产者消费者架构
  • 成都瀚网科技:如何有效运营抖店来客呢?
  • iMazing2.17.3免费苹果手机备份还原助手
  • sql查询到了数据但是实体类个别字段为null(映射失败)
  • 配置VScode开发环境-CUDA编程
  • openGauss学习笔记-101 openGauss 数据库管理-管理数据库安全-客户端接入之用SSH隧道进行安全的TCP/IP连接
  • STM32如何使用中断?
  • 用于物体识别和跟踪的下游任务自监督学习-2-(计算机视觉中的距离度量+损失函数)
  • 热成像仪的工作原理及在工业设备状态监测中的应用
  • 什么是库存管理?无需Excel,2023年这几款大热库存管理软件你get了吗?
  • c# xml 参数配置表的使用
  • ubuntu20.04 nerf Instant-ngp
  • 隐写术--python隐写