1、申 请 I D:lang1yd
2、个人邮箱:1535517520@qq.com
3、原创技术文章:重做论坛的Synaptics病毒修复工具
一,为什么要重做Synaptics修复工具
很久很久以前,个人电脑和多个U盘上就被 Synaptics病毒 攻占了,使用U盘时经常会传给其它电脑,或被其它电脑上的杀毒软件以病毒之名把U盘上的专用工具给删除了,是直接删除而不是恢复文件,发现中着后,个人以前的做法时, 将Synaptics 病毒的源目录中的exe文件,用个txt代替后修复权限,从而使病毒不能运行.
前些天,又被这个病毒唤醒了,就在网上搜罗. 下载到一个本论坛出品的Synaptics病毒专杀工具,
[PC样本分析] Synaptics蠕虫病毒感染解决方案https://www.52pojie.cn/forum.php ... hlight=%C8%E4%B3%E6
由于权限所限,只能看到第一页.下载了一个1.1.1.1版本的, 也不晓得是不是最新的文件,但是使用过后,正如作者说所:
目前已知问题,对某些exe文件会无法恢复,问题发生的原因不明,程序会自动跳过该类文件但是病毒还在要小心
对长路径名的文件无法处理,这个是Win系统的通病,我也不清楚病毒是怎么做到对长路径和长文件名的感染的
被感染的xlsm文件恢复还处于验证阶段,需要你们的测试结果
同一时间,搜索到一篇文章 , 偷梁换柱:谨防“Synaptics”蠕虫病毒 https://www.pianshen.com/article/6958951019/
知道了病毒的运作过程.于是用下载的专杀工具,效果感觉很好, U盘空间顿时空了两三百兆出来, 根据工具中扫描时提示的目录信息,我再到U盘的相关目录中去查看,
用exeScope工具来检查某些文件时,发现还是存在感染文件,但是专杀工具识别不了.下面以 病毒样品文件 : Oem7F7.exe (感染文件大小:1.58M,原始文件大小:881K)来测试,
下面是exeScope载入后的结果,确认是感染文件,其中RC数据中DESCRIPTION,EXERESX,EXEVSNX 的内容为 :
DESCRIPTION:
000B139C:53 00 79 00 6E 00 61 00 70 00 74 00 69 00 63 00 S.y.n.a.p.t.i.c.
000B13AC:73 00 20 00 50 00 6F 00 69 00 6E 00 74 00 69 00 s. .P.o.i.n.t.i.
000B13BC:6E 00 67 00 20 00 44 00 65 00 76 00 69 00 63 00 n.g. .D.e.v.i.c.
000B13CC:65 00 20 00 44 00 72 00 69 00 76 00 65 00 72 00 e. .D.r.i.v.e.r.
000B13DC:00 00 00 00 ....
EXERESX:
000B13F0:4D 5A 50 00 02 00 00 00 04 00 0F 00 FF FF 00 00 MZP.............
000B1400:B8 00 00 00 00 00 00 00 40 00 1A 00 00 00 00 00 ?......@.......
000B1410:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000B1420:00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 ................
000B1430:BA 10 00 0E 1F B4 09 CD 21 B8 01 4C CD 21 90 90 ?...???L?悙
000B1440:54 68 69 73 20 70 72 6F 67 72 61 6D 20 6D 75 73 This program mus
000B1450:74 20 62 65 20 72 75 6E 20 75 6E 64 65 72 20 57 t be run under W
000B1460:69 6E 33 32 0D 0A 24 37 00 00 00 00 00 00 00 00 in32..$7.........
.....
0018D9E0:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
EXEVSNX:
0018D9F0:31 30 36 106
在 exeScope将资源中的EXERESX内容导出为BIN文件后,直接就是原始文件, 于是准备写一个脚本调用exeScope.exe 来批量导出原始文件, 成是成功了,但是要附带一个exeScope.exe程序在脚本中也不像个事儿.
于是准备根据前辈的分析报告自己来写一个专杀的工具. 顺便用exeinfo PE查看了下载的专杀工具的编译工具是 [Linker 48 ] - MS Visual C# / Basic.NET ] - EPToken :0600001E.于是丢到 dnSpy中参考借鉴.经过分析源码,发现扫描不全的原因有:
其一: 专杀工具先取源病毒文件/或感染文件的属性中的描述字段信息作为病毒的特征码,若不能获取到 versionInfo.FileDescription,就不能确定病毒特征码,原代码摘要:
[C#] 纯文本查看 复制代码
string text ="C:\\ProgramData\\Synaptics\\Synaptics.exe";
....
FileVersionInfo versionInfo =FileVersionInfo.GetVersionInfo(text);
CS$<>8__locals1.SynapticsDescription =versionInfo.FileDescription;
....
FileVersionInfo versionInfo2 =FileVersionInfo.GetVersionInfo(openFileDialog.FileName);
CS$<>8__locals1.SynapticsDescription =versionInfo2.FileDescription;
...
if (string.IsNullOrEmpty(CS$<>8__locals1.SynapticsDescription))
{
MessageBox.Show("获得病毒描述信息失败,退出程序");
return;
}
用上面的Oem7F7.exe来测试时,会报错: 获得病毒描述信息失败,退出程序 .,然后直接退出. 其二, 判断是否病毒的标准是以文件的描述字段是不是匹配病毒源/或感染文件 的描述字段, 另外 不能取到检测文件的描述信息,也会跳过该文件,原代码摘要:
[C#] 纯文本查看 复制代码
FileVersionInfo versionInfo =FileVersionInfo.GetVersionInfo(text);
if (versionInfo.FileDescription == null)
{
continue;
}
if(versionInfo.FileDescription.StartsWith(A_1.SynapticsDescription))
{
.... }
}
用下面的 C# 来验证专杀工具中的代码检测样品病毒文件描述 的返回值:
[C#] 纯文本查看 复制代码
private voidbutton7_Click(object sender, EventArgs e)
{
stringtext = textBox8.Text;
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(text);
stringdesc = versionInfo.FileDescription;
if ( descis null)
Console.WriteLine($"文件:\n{text}\n是否存在: {File.Exists(text)}\n描述信息为:\n{desc}\n描述为:\nnull");
else
Console.WriteLine($"文件:\n{text}\n是否存在: {File.Exists(text)}\n描述信息为:\n{desc}\n描述信息长度为: {desc.Length}");
}
返回值 :
文件:F:\下载\Download\病毒\Synaptics病毒\病毒样本\Oem7F7.exe是否存在: True描述信息为: 描述为:null
结果是取不到描述值, 然后随机试了下其它exe文件, 发现很多文件都取不到描述值,如大家熟悉的程序IDA的卸载程序,exeScope.exe,,也有能取到描述的文件如ida64.exe,下面列举几个验证结果:
文件:D:\ProgramFiles\OllyDbg\IDA\uninstall.exe是否存在: True描述信息为: 描述信息长度为:0
文件:D:\Program Files\eXeScope\eXeScope.exe是否存在: True描述信息为: 描述信息长度为:0
文件:D:\Program Files\ollydbg\IDA\ida64.exe是否存在: True描述信息为:The Interactive Disassembler描述信息长度为:28
所以这会漏检很大一部分文件. 这里测试了几个不同的文件,发现病毒文件取的返回值是null,而不是病毒文件没取到值时,返回不是null,不知这个是不是个例.
其三,文件/文件夹遍历递归时,try语句位置不当, 会使遍历过程中遇到错误后,未遍历的文件会跳过,原代码:
[C#] 纯文本查看 复制代码
internal static void <Main>g__AddDirFiles|4_2(stringdir, ref Program.<>c__DisplayClass4_1 A_1)
{
try
{
DirectoryInfodirectoryInfo = new DirectoryInfo(dir);
List<FileInfo>list = new List<FileInfo>();
list.AddRange(directoryInfo.GetFiles("*.exe"));
list.AddRange(directoryInfo.GetFiles("*.xlsm"));
foreach (FileInfofileInfo in list)
{
A_1.files.Add(fileInfo.FullName);
}
DirectoryInfo[]directories = directoryInfo.GetDirectories();
for (int i = 0; i <directories.Length; i++)
{
Program.<Main>g__AddDirFiles|4_2(directories[i].FullName, ref A_1);
}
}
catch (UnauthorizedAccessException)
{
}
}
同样以c#测试一个明显的例子:
[C#] 纯文本查看 复制代码
private voidbutton6_Click(object sender, EventArgs e)
{
intlastint = 0;
try
{
for (int i = -7; i < 10; i++)
{
lastint = i;
Console.WriteLine(55 / i);
}
}
catch(System.DivideByZeroException)
{
Console.WriteLine("发生了除以零的异常,i 的值为: 0");
}
catch(Exception ex)
{
Console.WriteLine("发生了其他异常: " + ex.Message);
}
Console.WriteLine("i 最后的值为: " + lastint);
}
测试结果:
-7
-9
-11
-13
-18
-27
-55
引发的异常:“System.DivideByZeroException”(位于 Synaptics病毒修复工具.exe 中)发生了除以零的异常,i 的值为: 0
i 最后的值为: 0
用除0测试try,确定抛错后会跳出遍历,后面没有遍历的值会漏掉.所以原程序将try放在foreach外也可能会漏掉一些文件.
因为我看到的帖子时间是2019年12月份发表的,没有权限看后边的页,也不知道作者有没有修复上面的bug,也找不到更好的专杀工具,就参考这个专杀工具重制一个专杀工具.
二.程序设计思路
既然知道有以上三个bug,就先依次打补丁
1. 取病毒特征码
分析病毒文件:用exeScope检测多个感染文件和病毒原型文件,都有 RCDATA中 DESCRIPTION字段,且 值都为 “S y n a p t ic s P o i n t i n g D e v i c e D r i v er “
感染文件DESCRIPTION 字段的偏移地址是 0x000B139C-0x000B13DF, 病毒原型DESCRIPTION字段的偏移地址是 0x000B135C - 0x000B139F,
感染的:
000B139C:53 00 79 00 6E 00 6100 70 00 74 00 69 00 63 00 S.y.n.a.p.t.i.c.
000B13AC:73 00 20 00 50 00 6F00 69 00 6E 00 74 00 69 00 s..P.o.i.n.t.i.
000B13BC:6E 00 67 00 20 00 4400 65 00 76 00 69 00 63 00 n.g..D.e.v.i.c.
000B13CC:65 00 20 00 44 00 7200 69 00 76 00 65 00 72 00 e..D.r.i.v.e.r.
000B13DC:0000 00 00 ....
原型:
000B135C:53 00 79 00 6E 00 6100 70 00 74 00 69 00 63 00 S.y.n.a.p.t.i.c.
000B136C:73 00 20 00 50 00 6F00 69 00 6E 00 74 00 69 00 s..P.o.i.n.t.i.
000B137C:6E 00 67 00 20 00 4400 65 00 76 00 69 00 63 00 n.g..D.e.v.i.c.
000B138C:65 00 20 00 44 00 7200 69 00 76 00 65 00 72 00 e..D.r.i.v.e.r.
000B139C: 00 00 00 00 ....
可以将里面的二进制数据做为病毒的特征码.
[C#] 纯文本查看 复制代码
private static byte[] CreateVirDESCRIPTION()
{
//stringvir0 = "S y n a p t i c s P o i n t i n g D e v ic e D r i v e r ";
//stringvir1 ="53-00-79-00-6E-00-61-00-70-00-74-00-69-00-63-00-73-00-20-00-50-00-6F-00-69-00-6E-00-74-00-69-00-6E-00-67-00-20-00-44-00-65-00-76-00-69-00-63-00-65-00-20-00-44-00-72-00-69-00-76-00-65-00-72-00-00-00-00-00";
stringvir2 ="530079006E00610070007400690063007300200050006F0069006E00740069006E0067002000440065007600690063006500200044007200690076006500720000000000";
stringhexString = vir2;
intbyteCount = vir2.Length / 2;
byte[]virDESCRIPTION = new byte[byteCount];
for (inti = 0; i < byteCount; i++)
{
virDESCRIPTION[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
Console.WriteLine("病毒特征码长度:" + virDESCRIPTION.Length);
Console.WriteLine(BitConverter.ToString(virDESCRIPTION));
returnvirDESCRIPTION;
}
2.病毒文件匹配方式
这里就有两种方式来检测:
A用文件流方式,直接读取 0x000B139C -0x000B13DF 这一段地址来,来与 特征码匹配.
B用win32api读取文件的Rcdata,就是原专杀工具中导出exe的方法.
方式A:
[C#] 纯文本查看 复制代码
private static byte[]ReadFileBinMark(string filePath)
{ //检测模式0: BIN 以二进制读取文件的特征值
intstartAddress = 0x000B139C;
intendAddress = 0x000B13DF;
byte[]buffer = ReadFileBin(filePath, startAddress, endAddress);
returnbuffer;
}
private static byte[]ReadFileBin(string filePath, int startAddress, int endAddress)
{//以二进制读取文件
intlength = endAddress - startAddress + 1;
// 创建一个足够大的缓冲区来存储数据
byte[]buffer = new byte[length];
// 定位到文件的起始地址并读取数据
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open,FileAccess.Read,FileShare.ReadWrite))//添加 FileShare.ReadWrite参数来读取已被其它程序打开的文件
{
fs.Position = startAddress;
fs.Read(buffer, 0, length);
}
}
catch(IOException ex)
{
Console.WriteLine("无法读取文件: " + filePath + "\n\n" +ex.Message);
MessageBox.Show(ex.Message);
}
returnbuffer;
}
方式B:
[Asm] 纯文本查看 复制代码
private static byte[] ResGetRCDATA(string file, string[]rcLebals,bool limited = false) // privateList<byte[]> ResGetRCDATAs(string file, string[] infoNames)
{ //获取 RCDATA 中 多个字段的二进制 的值 并返回连接到一起的整串, "#10" 是指 RCDATA,
if(rcLebals.Length > 0)
{
// List<byte[]> rlt = new List<byte[]>(); // 创建 一个元素为byte[]的集合,用于接收结果
List<byte> rltByte = new List<byte>();
IntPtr module = IntPtr.Zero;
module = WinApi.LoadLibraryEx(file, IntPtr.Zero, flags[0]);
string copyFile = "";
for (int iti = 0;iti <5;iti++) //尝试5次
{
Console.WriteLine($"ResGetRCDATA LoadLibraryEx--flag: {flags[0]}--IntPtr module : {module}");
if (module == IntPtr.Zero)
{
// 加载失败,获取错误代码
int errorCode =Marshal.GetLastWin32Error();
Console.WriteLine("ResGetRCDATA LoadLibraryEx --IntPtrmodule -- errorCode :" + errorCode);
DialogResult dRlt =MessageBox.Show("文件加载失败 Win32ErrorCode: " + errorCode+ "\n\n" + file+"\n\n是否 尝试其它方式\n\n" +
"是----复制源文件并读取镜像文件,文件大小:["+ GetFileSize(file) + "]\n\n" +
"否----调用 BIN 方式--待完善\n\n" +
"取消--忽略该文件", $"文件加载失败,剩余尝试次数:{5-iti}", MessageBoxButtons.YesNoCancel);
switch (dRlt)
{
caseDialogResult.Yes:
Random random = new Random();
StringBuilder randomStr = new StringBuilder();
for(int i = 0; i < 5; i++)
{
int randomIndex = random.Next(chars.Length);
randomStr.Append(chars[randomIndex]);
}
copyFile = file+$".{randomStr}";
File.Copy(file, copyFile,true);
module = WinApi.LoadLibraryEx(copyFile, IntPtr.Zero,flags[0]); //flags[0] = 0x60
if (module == IntPtr.Zero) //如果还是加载失败,就立即删除复制的文件,不然就在读取完后再删除.
{
WinApi.FreeLibrary(module);
File.Delete(copyFile);
Console.WriteLine("文件已被删除: " + copyFile);
}
break;
default:
return new byte[] { };
}
}
else
{
break;
}
}
foreach (string rcLebal in rcLebals)
{
IntPtr resourceInfo =WinApi.FindResourceEx(module, "#10", rcLebal, 0);
uint size = WinApi.SizeofResource(module,resourceInfo);
IntPtr source =WinApi.LockResource(WinApi.LoadResource(module, resourceInfo));
if (size != 0U)
{
if(limited && size >200) { size = 16; } //有限制时,且超长时,限制为16字节,针对 "EXERESX" 的摘要信息
byte[] array = new byte[size];
Marshal.Copy(source, array, 0,array.Length);
rltByte.AddRange(array);// rlt.Add(array);
}
}
WinApi.FreeLibrary(module);
if (File.Exists(copyFile))
{
File.Delete(copyFile);//如果存在复制的临时文件,就删除
Console.WriteLine("文件已被删除: " + copyFile);
}
return rltByte.ToArray();
}
returnnew byte[] { }; // new List<byte[]>()
}
3.修改 检测文件及文件夹遍历递归方式,将try放在for/foreach 内部
[C#] 纯文本查看 复制代码
private void ScanDirectoryRecursively(string directory, boolisTest, object sender, ref int scanCount, ref int virsCount)
{
if(reqStop)
{
//(sender as BackgroundWorker).ReportProgress(3);//退出
Console.WriteLine("ScanDirectoryRecursively--return");
return;
}
foreach(string file in Directory.GetFiles(directory,"*.exe")) //后续要添加xlsm ,不要添加 SearchOption.AllDirectories 参数
{
try
{
scanCount++;
(sender as BackgroundWorker).ReportProgress(1,file + '|' + scanCount);
if (!file.ToLower().EndsWith(".exe"))
continue; //过滤以 ".exe_bak" 结尾的文件
if (skipCache &&file.Contains("\\._cache_"))
continue; //过滤含"\\._cache_" 的文件
//判断文件大小是不是 < 740K
long.TryParse(GetFileSize(file, false),out longfileSizeB);
if (fileSizeB < VIR_FILE_MIN_SIZE)
continue; //过滤小于740K 的文件
if (isPause)
{ //暂停
if(PauseWork(sender)) return;
}
Console.WriteLine(file);
bool isVirus = false; //是否感染
if (scanMode == 0)
{ //快速匹配
if(ReadFileBinMark(file).SequenceEqual(virDESCRIPTION))
{
isVirus = true;
}
}
else if (ResGetRCDATA(file,resDesLabel).SequenceEqual(virDESCRIPTION))
{ //精确匹配
isVirus = true;
}
if (isVirus)
{
virsCount++;
(sender as BackgroundWorker).ReportProgress(2,file + '|' + virsCount);
//int tableIndex =0; //用添加的方式时,新项依次追加在后面,要scollView查看最后项,用insert直接插入时,最新的项在最前面
// 将病毒信息添加到DataGridView中 // 使用Invoke来在UI线程上执行添加行的操作
dataGridView1.Invoke(new Action(()=>
{
//tableIndex=dataGridView1.Rows.Add((dataGridView1.Rows.Count).ToString(),file, Rlt[0]);
dataGridView1.Rows.Insert(0, (dataGridView1.Rows.Count).ToString(), file,Rlt[0]);
}));
// 尝试修复文件
string repairResult =RepairFile(file, isTest);
// 更新DataGridView中的修复结果 // 使用Invoke来在UI线程上执行更新行的操作
dataGridView1.Invoke(new Action(()=>
{
//dataGridView1.Rows[tableIndex].Cells[2].Value = repairResult;
dataGridView1.Rows[0].Cells[2].Value = repairResult;
}));
}
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("file无法访问某些文件夹,因为没有足够的权限。" + file);
if (checkBox_showErr.Checked)
{
MessageBox.Show("file无法访问某些文件夹,因为没有足够的权限。" + file);
}
}
catch (Exception ex)
{
Console.WriteLine("file发生了其他错误: " + ex.Message + file);
if (checkBox_showErr.Checked)
{
MessageBox.Show("file发生了其他错误: " + ex.Message + file);
}
}
}
foreach(string dir in Directory.GetDirectories(directory))//不添加 SearchOption.AllDirectories 参数
{
if (reqStop)
{
//(sender as BackgroundWorker).ReportProgress(3);//退出
Console.WriteLine("ScanDirectoryRecursively--dir--return");
return;
}
try
{ // 检查是否是junction文件夹
if ((WinApi.GetFileAttributes(dir) & FILE_ATTRIBUTE_REPARSE_POINT)== FILE_ATTRIBUTE_REPARSE_POINT)
{
Console.WriteLine($"Skippingjunction: {dir}");
continue;
}
ScanDirectoryRecursively(dir, isTest, sender,ref scanCount, ref virsCount);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("dir无法访问某些文件夹,因为没有足够的权限。" + dir);
if (checkBox_showErr.Checked)
{
MessageBox.Show("dir无法访问某些文件夹,因为没有足够的权限。" + dir);
}
}
catch (Exception ex)
{
Console.WriteLine("dir发生了其他错误: " + ex.Message + dir);
if (checkBox_showErr.Checked)
{
MessageBox.Show("dir发生了其他错误: " + ex.Message + dir);
}
}
}
}
4.其它个性功能的定制就不在这里赘述了.只发2个完成的截图,都是原专杀漏检的病毒.
5.待完善的:
1)没有感染的xlsm文件, 不能做样本分析, 将病毒 丢到vmware后,也没有得到感染的xlsm病毒文件, xlsm文件的检测还不可用.找到样品了再完善
2)用res方式检测时,若文件已初步写入占用,如被exeScope打开,会检测失败,造成漏检,然后会进行弹窗复制检测处理
3)用BIN方式检测时.会过滤synaptics原型文件, 可用清理环境过能清理病毒原型 "C:\\ProgramData\\Synaptics\\Synaptics.exe"
4)鉴于手中只有两个病毒原型文件,可能其它版本的synaptics病毒会失效.
5)制作匆忙,其它不足之处,请不吝赐教.
6.其它说明:本人不是专业程序员,也不从事软件开发的工作,都是以自学教程为主,博而不专,只是以个人兴趣,有时会写点工具方便自己方便他人.也给几个软件, 研究着写过注册机,.加入论坛目的也是自己兴趣爱好,可以学习到更多的经验教程.
这是个甩不掉的小尾巴: S y n a p t i c s P o i nt i n g D e v i c e D r i v e r
一,为什么要重做Synaptics修复工具
很久很久以前,个人电脑和多个U盘上就被 Synaptics病毒 攻占了,使用U盘时经常会传给其它电脑,或被其它电脑上的杀毒软件以病毒之名把U盘上的专用工具给删除了,是直接删除而不是恢复文件,发现中着后,个人以前的做法时, 将Synaptics 病毒的源目录中的exe文件,用个txt代替后修复权限,从而使病毒不能运行.
前些天,又被这个病毒唤醒了,就在网上搜罗. 下载到一个本论坛出品的Synaptics病毒专杀工具,
[PC样本分析] Synaptics蠕虫病毒感染解决方案https://www.52pojie.cn/forum.php ... hlight=%C8%E4%B3%E6
由于权限所限,只能看到第一页.下载了一个1.1.1.1版本的, 也不晓得是不是最新的文件,但是使用过后,正如作者说所:
目前已知问题,对某些exe文件会无法恢复,问题发生的原因不明,程序会自动跳过该类文件但是病毒还在要小心
对长路径名的文件无法处理,这个是Win系统的通病,我也不清楚病毒是怎么做到对长路径和长文件名的感染的
被感染的xlsm文件恢复还处于验证阶段,需要你们的测试结果
同一时间,搜索到一篇文章, 偷梁换柱:谨防“Synaptics”蠕虫病毒 https://www.pianshen.com/article/6958951019/
知道了病毒的运作过程.于是用下载的专杀工具,效果感觉很好, U盘空间顿时空了两三百兆出来, 根据工具中扫描时提示的目录信息,我再到U盘的相关目录中去查看,
用exeScope工具来检查某些文件时,发现还是存在感染文件,但是专杀工具识别不了.下面以 病毒样品文件 : Oem7F7.exe(感染文件大小:1.58M,原始文件大小:881K)来测试,
下面是exeScope载入后的结果,确认是感染文件,其中RC数据中DESCRIPTION, EXERESX,EXEVSNX 的内容为 :
DESCRIPTION:
000B139C: 53 0079 00 6E 00 61 00 70 00 74 00 69 00 63 00 S.y.n.a.p.t.i.c.
000B13AC: 73 0020 00 50 00 6F 00 69 00 6E 00 74 00 69 00 s. .P.o.i.n.t.i.
000B13BC: 6E 0067 00 20 00 44 00 65 00 76 00 69 00 63 00 n.g. .D.e.v.i.c.
000B13CC: 65 0020 00 44 00 72 00 69 00 76 00 65 00 72 00 e. .D.r.i.v.e.r.
000B13DC: 00 0000 00 ....
EXERESX:
000B13F0: 4D 5A50 00 02 00 00 00 04 00 0F 00 FF FF 00 00 MZP.............
000B1400: B8 00 0000 00 00 00 00 40 00 1A 00 00 00 00 00 ?......@.......
000B1410: 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000B1420: 00 0000 00 00 00 00 00 00 00 00 00 00 01 00 00 ................
000B1430: BA 1000 0E 1F B4 09 CD 21 B8 01 4C CD 21 90 90 ?...???L?悙
000B1440: 54 6869 73 20 70 72 6F 67 72 61 6D 20 6D 75 73 This program mus
000B1450: 74 2062 65 20 72 75 6E 20 75 6E 64 65 72 20 57 t be run under W
000B1460: 69 6E33 32 0D 0A 24 37 00 00 00 00 00 00 00 00 in32..$7.........
.....
0018D9E0: 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
EXEVSNX:
0018D9F0: 31 3036 106
在 exeScope将资源中的EXERESX内容导出为BIN文件后,直接就是原始文件, 于是准备写一个脚本调用exeScope.exe来批量导出原始文件, 成是成功了,但是要附带一个exeScope.exe程序在脚本中也不像个事儿.
于是准备根据前辈的分析报告自己来写一个专杀的工具. 顺便用exeinfoPE查看了下载的专杀工具的编译工具是 [ Linker 48 ] - MS Visual C# /Basic.NET ] - EPToken : 0600001E.于是丢到 dnSpy中参考借鉴.经过分析源码,发现扫描不全的原因有:
其一: 专杀工具先取源病毒文件/或感染文件的属性中的描述字段信息作为病毒的特征码,若不能获取到 versionInfo.FileDescription,就不能确定病毒特征码,原代码摘要:
[C#] 纯文本查看 复制代码
string text ="C:\\ProgramData\\Synaptics\\Synaptics.exe";
....
FileVersionInfo versionInfo =FileVersionInfo.GetVersionInfo(text);
CS$<>8__locals1.SynapticsDescription =versionInfo.FileDescription;
....
FileVersionInfo versionInfo2 =FileVersionInfo.GetVersionInfo(openFileDialog.FileName);
CS$<>8__locals1.SynapticsDescription =versionInfo2.FileDescription;
...
if(string.IsNullOrEmpty(CS$<>8__locals1.SynapticsDescription))
{
MessageBox.Show("获得病毒描述信息失败,退出程序");
return;
}
用上面的Oem7F7.exe来测试时,会报错: 获得病毒描述信息失败,退出程序 .,然后直接退出. 其二, 判断是否病毒的标准是以 文件的描述字段是不是匹配病毒源/或感染文件 的描述字段, 另外 不能取到检测文件的描述信息,也会跳过该文件,原代码摘要:
[C#] 纯文本查看 复制代码
FileVersionInfo versionInfo =FileVersionInfo.GetVersionInfo(text);
if (versionInfo.FileDescription == null)
{
continue;
}
if(versionInfo.FileDescription.StartsWith(A_1.SynapticsDescription))
{
.... }
}
用下面的 C# 来验证专杀工具中的代码 检测样品病毒文件描述 的返回值:
[C#] 纯文本查看 复制代码
private voidbutton7_Click(object sender, EventArgs e)
{
stringtext = textBox8.Text;
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(text);
stringdesc = versionInfo.FileDescription;
if ( descis null)
Console.WriteLine($"文件:\n{text}\n是否存在: {File.Exists(text)}\n描述信息为:\n{desc}\n描述为:\nnull");
else
Console.WriteLine($"文件:\n{text}\n是否存在: {File.Exists(text)}\n描述信息为:\n{desc}\n描述信息长度为: {desc.Length}");
}
返回值 :
文件:F:\下载\Download\病毒\Synaptics病毒\病毒样本\Oem7F7.exe是否存在: True描述信息为: 描述为:null
结果是取不到描述值, 然后随机试了下其它exe文件, 发现很多文件都取不到描述值,如大家熟悉的程序IDA的卸载程序,exeScope.exe,,也有能取到描述的文件如ida64.exe,下面列举几个验证结果:
文件:D:\ProgramFiles\ollydbg\IDA\uninstall.exe是否存在: True描述信息为: 描述信息长度为:0
文件:D:\Program Files\eXeScope\eXeScope.exe是否存在: True描述信息为: 描述信息长度为:0
文件:D:\Program Files\ollydbg\IDA\ida64.exe是否存在: True描述信息为:The InteractiveDisassembler描述信息长度为:28
所以这会漏检很大一部分文件. 这里测试了几个不同的文件,发现病毒文件取的返回值是null,而不是病毒文件没取到值时,返回不是null,不知这个是不是个例.
其三,文件/文件夹遍历递归时,try语句位置不当, 会使遍历过程中遇到错误后,未遍历的文件会跳过,原代码:
[C#] 纯文本查看 复制代码
internal static void <Main>g__AddDirFiles|4_2(stringdir, ref Program.<>c__DisplayClass4_1 A_1)
{
try
{
DirectoryInfodirectoryInfo = new DirectoryInfo(dir);
List<FileInfo>list = new List<FileInfo>();
list.AddRange(directoryInfo.GetFiles("*.exe"));
list.AddRange(directoryInfo.GetFiles("*.xlsm"));
foreach (FileInfofileInfo in list)
{
A_1.files.Add(fileInfo.FullName);
}
DirectoryInfo[]directories = directoryInfo.GetDirectories();
for (int i = 0; i <directories.Length; i++)
{
Program.<Main>g__AddDirFiles|4_2(directories[i].FullName, ref A_1);
}
}
catch (UnauthorizedAccessException)
{
}
}
同样以c#测试一个明显的例子:
[C#] 纯文本查看 复制代码
private voidbutton6_Click(object sender, EventArgs e)
{
intlastint = 0;
try
{
for (int i = -7; i < 10; i++)
{
lastint = i;
Console.WriteLine(55 / i);
}
}
catch(System.DivideByZeroException)
{
Console.WriteLine("发生了除以零的异常,i 的值为: 0");
}
catch(Exception ex)
{
Console.WriteLine("发生了其他异常: " + ex.Message);
}
Console.WriteLine("i 最后的值为: " + lastint);
}
测试结果:
-7
-9
-11
-13
-18
-27
-55
引发的异常:“System.DivideByZeroException”(位于 Synaptics病毒修复工具.exe 中)发生了除以零的异常,i 的值为: 0
i 最后的值为: 0
用除0测试try,确定抛错后会跳出遍历,后面没有遍历的值会漏掉.所以原程序将try放在foreach外也可能会漏掉一些文件.
因为我看到的帖子时间是2019年12月份发表的,没有权限看后边的页,也不知道作者有没有修复上面的bug,也找不到更好的专杀工具,就参考这个专杀工具重制一个专杀工具.
二.程序设计思路
既然知道有以上三个bug,就先依次打补丁
1. 取病毒特征码
分析病毒文件:用exeScope检测多个感染文件和病毒原型文件,都有 RCDATA中 DESCRIPTION字段,且 值都为 “S y n a p t i c s P o i n t i n g D e v i ce D r i v e r “
感染文件DESCRIPTION 字段的偏移地址是0x000B139C -0x000B13DF, 病毒原型DESCRIPTION字段的偏移地址是 0x000B135C - 0x000B139F,
感染的:
000B139C: 53 0079 00 6E 00 6100 70 00 74 00 69 00 63 00 S.y.n.a.p.t.i.c.
000B13AC: 73 0020 00 50 00 6F00 69 00 6E 00 74 00 69 00 s..P.o.i.n.t.i.
000B13BC: 6E 0067 00 20 00 4400 65 00 76 00 69 00 63 00 n.g..D.e.v.i.c.
000B13CC: 65 0020 00 44 00 7200 69 00 76 00 65 00 72 00 e..D.r.i.v.e.r.
000B13DC:00 00 0000 ....
原型:
000B135C: 53 0079 00 6E 00 6100 70 00 74 00 69 00 63 00 S.y.n.a.p.t.i.c.
000B136C: 73 0020 00 50 00 6F00 69 00 6E 00 74 00 69 00 s..P.o.i.n.t.i.
000B137C: 6E 0067 00 20 00 4400 65 00 76 00 69 00 63 00 n.g..D.e.v.i.c.
000B138C: 65 0020 00 44 00 7200 69 00 76 00 65 00 72 00 e..D.r.i.v.e.r.
000B139C: 00 0000 00 ....
可以将里面的二进制数据做为病毒的特征码.
[C#] 纯文本查看 复制代码
private static byte[] CreateVirDESCRIPTION()
{
//stringvir0 = "S y n a p t i c s P o i n t i n g D e v ic e D r i v e r ";
//stringvir1 = "53-00-79-00-6E-00-61-00-70-00-74-00-69-00-63-00-73-00-20-00-50-00-6F-00-69-00-6E-00-74-00-69-00-6E-00-67-00-20-00-44-00-65-00-76-00-69-00-63-00-65-00-20-00-44-00-72-00-69-00-76-00-65-00-72-00-00-00-00-00";
stringvir2 = "530079006E00610070007400690063007300200050006F0069006E00740069006E0067002000440065007600690063006500200044007200690076006500720000000000";
stringhexString = vir2;
intbyteCount = vir2.Length / 2;
byte[]virDESCRIPTION = new byte[byteCount];
for (inti = 0; i < byteCount; i++)
{
virDESCRIPTION[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
Console.WriteLine("病毒特征码长度:" + virDESCRIPTION.Length);
Console.WriteLine(BitConverter.ToString(virDESCRIPTION));
returnvirDESCRIPTION;
}
2.病毒文件匹配方式
这里就有两种方式来检测:
A用文件流方式,直接读取 0x000B139C - 0x000B13DF 这一段地址来,来与 特征码匹配.
B用win32api读取文件的Rcdata,就是原专杀工具中导出exe的方法.
方式A:
[C#] 纯文本查看 复制代码
private static byte[]ReadFileBinMark(string filePath)
{ //检测模式0: BIN 以二进制读取文件的特征值
intstartAddress = 0x000B139C;
intendAddress = 0x000B13DF;
byte[]buffer = ReadFileBin(filePath, startAddress, endAddress);
returnbuffer;
}
private static byte[]ReadFileBin(string filePath, int startAddress, int endAddress)
{//以二进制读取文件
intlength = endAddress - startAddress + 1;
// 创建一个足够大的缓冲区来存储数据
byte[]buffer = new byte[length];
// 定位到文件的起始地址并读取数据
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open,FileAccess.Read,FileShare.ReadWrite))//添加 FileShare.ReadWrite参数来读取已被其它程序打开的文件
{
fs.Position = startAddress;
fs.Read(buffer, 0, length);
}
}
catch(IOException ex)
{
Console.WriteLine("无法读取文件: " + filePath + "\n\n" +ex.Message);
MessageBox.Show(ex.Message);
}
returnbuffer;
}
方式B:
[Asm] 纯文本查看 复制代码
private static byte[] ResGetRCDATA(string file, string[]rcLebals,bool limited = false) // privateList<byte[]> ResGetRCDATAs(string file, string[] infoNames)
{ //获取 RCDATA 中 多个字段的二进制 的值 并返回连接到一起的整串, "#10" 是指 RCDATA,
if(rcLebals.Length > 0)
{
// List<byte[]> rlt = new List<byte[]>(); // 创建 一个元素为byte[]的集合,用于接收结果
List<byte> rltByte = new List<byte>();
IntPtr module = IntPtr.Zero;
module = WinApi.LoadLibraryEx(file, IntPtr.Zero, flags[0]);
string copyFile = "";
for (int iti = 0;iti <5;iti++) //尝试5次
{
Console.WriteLine($"ResGetRCDATA LoadLibraryEx--flag: {flags[0]}--IntPtr module : {module}");
if (module == IntPtr.Zero)
{
// 加载失败,获取错误代码
int errorCode =Marshal.GetLastWin32Error();
Console.WriteLine("ResGetRCDATA LoadLibraryEx --IntPtrmodule -- errorCode :" + errorCode);
DialogResult dRlt =MessageBox.Show("文件加载失败 Win32ErrorCode: " + errorCode+ "\n\n" + file+"\n\n是否 尝试其它方式\n\n" +
"是----复制源文件并读取镜像文件,文件大小:["+ GetFileSize(file) + "]\n\n" +
"否----调用 BIN 方式--待完善\n\n" +
"取消--忽略该文件", $"文件加载失败,剩余尝试次数:{5-iti}", MessageBoxButtons.YesNoCancel);
switch (dRlt)
{
caseDialogResult.Yes:
Random random = new Random();
StringBuilder randomStr = new StringBuilder();
for(int i = 0; i < 5; i++)
{
int randomIndex = random.Next(chars.Length);
randomStr.Append(chars[randomIndex]);
}
copyFile = file+$".{randomStr}";
File.Copy(file, copyFile,true);
module = WinApi.LoadLibraryEx(copyFile, IntPtr.Zero,flags[0]); //flags[0] = 0x60
if (module == IntPtr.Zero) //如果还是加载失败,就立即删除复制的文件,不然就在读取完后再删除.
{
WinApi.FreeLibrary(module);
File.Delete(copyFile);
Console.WriteLine("文件已被删除: " + copyFile);
}
break;
default:
return new byte[] { };
}
}
else
{
break;
}
}
foreach (string rcLebal in rcLebals)
{
IntPtr resourceInfo =WinApi.FindResourceEx(module, "#10", rcLebal, 0);
uint size = WinApi.SizeofResource(module,resourceInfo);
IntPtr source = WinApi.LockResource(WinApi.LoadResource(module,resourceInfo));
if (size != 0U)
{
if(limited && size >200) { size = 16; } //有限制时,且超长时,限制为16字节,针对 "EXERESX" 的摘要信息
byte[] array = new byte[size];
Marshal.Copy(source, array, 0,array.Length);
rltByte.AddRange(array);// rlt.Add(array);
}
}
WinApi.FreeLibrary(module);
if (File.Exists(copyFile))
{
File.Delete(copyFile);//如果存在复制的临时文件,就删除
Console.WriteLine("文件已被删除: " + copyFile);
}
return rltByte.ToArray();
}
return newbyte[] { }; // new List<byte[]>()
}
3.修改 检测文件及文件夹遍历递归方式,将try放在for/foreach 内部
[C#] 纯文本查看 复制代码
private void ScanDirectoryRecursively(string directory, boolisTest, object sender, ref int scanCount, ref int virsCount)
{
if(reqStop)
{
//(sender as BackgroundWorker).ReportProgress(3);//退出
Console.WriteLine("ScanDirectoryRecursively--return");
return;
}
foreach(string file in Directory.GetFiles(directory,"*.exe")) //后续要添加xlsm ,不要添加 SearchOption.AllDirectories 参数
{
try
{
scanCount++;
(sender as BackgroundWorker).ReportProgress(1,file + '|' + scanCount);
if (!file.ToLower().EndsWith(".exe"))
continue; //过滤以 ".exe_bak" 结尾的文件
if (skipCache &&file.Contains("\\._cache_"))
continue; //过滤含"\\._cache_" 的文件
//判断文件大小是不是 < 740K
long.TryParse(GetFileSize(file, false),out longfileSizeB);
if (fileSizeB < VIR_FILE_MIN_SIZE)
continue; //过滤小于740K 的文件
if (isPause)
{ //暂停
if(PauseWork(sender)) return;
}
Console.WriteLine(file);
bool isVirus = false; //是否感染
if (scanMode == 0)
{ //快速匹配
if(ReadFileBinMark(file).SequenceEqual(virDESCRIPTION))
{
isVirus = true;
}
}
else if (ResGetRCDATA(file,resDesLabel).SequenceEqual(virDESCRIPTION))
{ //精确匹配
isVirus = true;
}
if (isVirus)
{
virsCount++;
(sender asBackgroundWorker).ReportProgress(2, file + '|' + virsCount);
//int tableIndex =0; //用添加的方式时,新项依次追加在后面,要scollView查看最后项,用insert直接插入时,最新的项在最前面
// 将病毒信息添加到DataGridView中 // 使用Invoke来在UI线程上执行添加行的操作
dataGridView1.Invoke(new Action(()=>
{
//tableIndex=dataGridView1.Rows.Add((dataGridView1.Rows.Count).ToString(),file, Rlt[0]);
dataGridView1.Rows.Insert(0, (dataGridView1.Rows.Count).ToString(), file,Rlt[0]);
}));
// 尝试修复文件
string repairResult =RepairFile(file, isTest);
// 更新DataGridView中的修复结果 // 使用Invoke来在UI线程上执行更新行的操作
dataGridView1.Invoke(new Action(()=>
{
//dataGridView1.Rows[tableIndex].Cells[2].Value= repairResult;
dataGridView1.Rows[0].Cells[2].Value = repairResult;
}));
}
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("file无法访问某些文件夹,因为没有足够的权限。" + file);
if (checkBox_showErr.Checked)
{
MessageBox.Show("file无法访问某些文件夹,因为没有足够的权限。" + file);
}
}
catch (Exception ex)
{
Console.WriteLine("file发生了其他错误: " + ex.Message + file);
if (checkBox_showErr.Checked)
{
MessageBox.Show("file发生了其他错误: " + ex.Message + file);
}
}
}
foreach(string dir in Directory.GetDirectories(directory))//不添加 SearchOption.AllDirectories 参数
{
if (reqStop)
{
//(sender asBackgroundWorker).ReportProgress(3);//退出
Console.WriteLine("ScanDirectoryRecursively--dir--return");
return;
}
try
{ // 检查是否是junction文件夹
if ((WinApi.GetFileAttributes(dir) &FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT)
{
Console.WriteLine($"Skippingjunction: {dir}");
continue;
}
ScanDirectoryRecursively(dir, isTest, sender,ref scanCount, ref virsCount);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("dir无法访问某些文件夹,因为没有足够的权限。" + dir);
if (checkBox_showErr.Checked)
{
MessageBox.Show("dir无法访问某些文件夹,因为没有足够的权限。" + dir);
}
}
catch (Exception ex)
{
Console.WriteLine("dir发生了其他错误: " + ex.Message + dir);
if (checkBox_showErr.Checked)
{
MessageBox.Show("dir发生了其他错误: " + ex.Message + dir);
}
}
}
}
4.其它个性功能的定制就不在这里赘述了.只发2个完成的截图,都是原专杀漏检的病毒.
5.待完善的:
1)没有感染的xlsm文件, 不能做样本分析, 将病毒 丢到vmware后,也没有得到感染的xlsm病毒文件, xlsm文件的检测还不可用.找到样品了再完善
2)用res方式检测时,若文件已初步写入占用,如被exeScope打开,会检测失败,造成漏检,然后会进行弹窗复制检测处理
3)用BIN方式检测时.会过滤synaptics原型文件, 可用清理环境过能清理病毒原型 "C:\\ProgramData\\Synaptics\\Synaptics.exe"
4)鉴于手中只有两个病毒原型文件,可能其它版本的synaptics病毒会失效.
5)制作匆忙,其它不足之处,请不吝赐教.
6.其它说明:本人不是专业程序员,也不从事软件开发的工作,都是以自学教程为主,博而不专,只是以个人兴趣,有时会写点工具方便自己方便他人.也给几个软件, 研究着写过注册机,.加入论坛目的也是自己兴趣爱好,可以学习到更多的经验教程.
这是个甩不掉的小尾巴: S y n a p t i c s P o i nt i n g D e v i c e D r i v e r
提交一直是说有非法字符...无语了.
|