Synaptics病毒修复工具
现在以 病毒样品文件 : 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,就不能确定病毒特征码,原代码摘要:
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时,会报错: 获得病毒描述信息失败,退出程序 ,然后退出工具.
其二, 判断是否病毒的标准是以文件的描述字段是不是匹配病毒源/或感染文件 的描述字段, 另外 不能取到检测文件的描述信息,也会跳过该文件,原代码摘要:
1
2
3
4
5
6
7
8
9
|
FileVersionInfo versionInfo =FileVersionInfo.GetVersionInfo(text); if (versionInfo.FileDescription == null ) { continue ; } if (versionInfo.FileDescription.StartsWith(A_1.SynapticsDescription)) { .... } |
用下面的 C# 来验证专杀工具中的代码检测样品病毒文件描述 的返回值:
01
02
03
04
05
06
07
08
09
10
|
private void button7_Click( object sender, EventArgs e) { string text = textBox8.Text; FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(text); string desc = versionInfo.FileDescription; if ( desc is null ) Console.WriteLine($ "文件:\n{text}\n是否存在: {File.Exists(text)}\n描述信息为:\n{desc}\n描述为:null" ); 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语句位置不当, 会使遍历过程中遇到错误后,未遍历的文件会跳过,原代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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#例子来测试一个明显的try,for逻辑:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
private void button6_Click( object sender, EventArgs e) { int lastint = 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月份发表的,现在都2024了,[开始还不是本站会员]没有权限看后边的页,也不知道作者有没有修复上面的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:00 00 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 ….
可以将里面的二进制数据做为病毒的特征码.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
[/size][size=2] private static byte [] CreateVirDESCRIPTION()[/size][size=2] {[/size][size=2] //string vir0 = "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 ";[/size][size=2] //string vir1 ="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";[/size][size=2] string vir2 = "530079006E00610070007400690063007300200050006F0069006E00740069006E0067002000440065007600690063006500200044007200690076006500720000000000" ;[/size] [size=2] string hexString = vir2;[/size][size=2] int byteCount = vir2.Length / 2;[/size][size=2] byte [] virDESCRIPTION = new byte [byteCount];[/size][size=2] for (inti = 0; i < byteCount; i++)[/size][size=2] {[/size][size=2] virDESCRIPTION[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);[/size][size=2] }[/size][size=2] Console.WriteLine( "病毒特征码长度:" + virDESCRIPTION.Length);[/size][size=2] Console.WriteLine(BitConverter.ToString(virDESCRIPTION));[/size][size=2] return virDESCRIPTION;[/size][size=2] }[/size][size=2] |
2.病毒文件匹配方式
这里就有两种方式来检测:
A 用文件流方式,直接读取 0x000B139C -0x000B13DF 这一段地址,来与 特征码匹配.
B 用win32api读取文件的Rcdata中的DESCRIPTION字段,就是原专杀工具中导出exe的方法.
方式A:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
[/size] [size=2] private static byte []ReadFileBinMark( string filePath)[/size] [size=2]{ //检测模式0: BIN 以二进制读取文件的特征值[/size] [size=2] int startAddress = 0x000B139C;[/size] [size=2] int endAddress = 0x000B13DF;[/size] [size=2] byte [] buffer = ReadFileBin(filePath, startAddress, endAddress);[/size] [size=2] return buffer;[/size] [size=2]}[/size] [size=2] private static byte []ReadFileBin( string filePath, int startAddress, int endAddress)[/size] [size=2]{ //以二进制读取文件[/size] [size=2] int length = endAddress - startAddress + 1;[/size] [size=2] // 创建一个足够大的缓冲区来存储数据[/size] [size=2] byte [] buffer = new byte [length];[/size] [size=2] // 定位到文件的起始地址并读取数据[/size] [size=2] try [/size] [size=2] {[/size] [size=2] using (FileStream fs = new FileStream(filePath, FileMode.Open,FileAccess.Read,FileShare.ReadWrite)) //添加 FileShare.ReadWrite参数来读取已被其它程序打开的文件[/size] [size=2] {[/size] [size=2] fs.Position = startAddress;[/size] [size=2] fs.Read(buffer, 0, length);[/size] [size=2] }[/size] [size=2] }[/size] [size=2] catch (IOException ex)[/size] [size=2] {[/size] [size=2] Console.WriteLine( "无法读取文件: " + filePath + "\n\n" +ex.Message);[/size] [size=2] MessageBox.Show(ex.Message);[/size] [size=2] }[/size] [size=2] return buffer;[/size] [size=2]}[/size] [size=2] |
方式B:
01
02
03
04
05
06
07
08
09
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
|
private static byte [] ResGetRCDATA( string file, string []rcLabels, bool limited = false ) { //获取 RCDATA 中 多个字段的二进制 的值 并返回连接到一起的整串, "#10" 是指 RCDATA, if (rcLabels.Length > 0) { 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) { case DialogResult.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 rcLabel in rcLabels) { IntPtr resourceInfo =WinApi.FindResourceEx(module, "#10" , rcLabel, 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); } } WinApi.FreeLibrary(module); if (File.Exists(copyFile)) { File.Delete(copyFile); //如果存在复制的临时文件,就删除 Console.WriteLine( "文件已被删除: " + copyFile); } return rltByte.ToArray(); } return new byte [] { }; } |
3.修改 检测文件及文件夹遍历递归方式,将try放在for/foreach 内部
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
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原型文件, 因为DESCRIPTION字段的偏移不一样,没有做扩展检测,可用清理环境功能清理病毒原型 “C:\\ProgramData\\Synaptics\\Synaptics.exe”
4)鉴于手中只有两个病毒原型文件,可能对其它版本的synaptics病毒会失效.
5)制作匆忙,其它不足之处,请不吝赐教,后面有时间再做一个完善的版本给需要的人.
一旦您浏览本站,即表示您已接受以下条约:
1.使用辅助可能会违反游戏协议,甚至违法,用户有权决定使用,并自行承担风险;
2.本站辅助严禁用于任何形式的商业用途,若被恶意贩卖,利益与本站无关;
3.本站为非营利性网站,但为了分担服务器等运营费用,收费均为赞助,没有任何利益收益。
死神科技 » Synaptics病毒修复工具