好友
阅读权限 20
听众
最后登录 1970-1-1
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子! 病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途! 禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!
本帖最后由 lang1yd 于 2024-9-28 02:20 编辑
一、为什么要重做Synaptics修复工具
很久很久以前,个人电脑和多个U盘上就被 Synaptics病毒 攻占了,使用U盘时经常会传给其它电脑,或被其它电脑上的杀毒软件以病毒之名
把U盘上的专用工具给删除了,是直接删除而不是恢复文件.
发现中着后,个人以前的做法时, 将Synaptics 病毒的源目录中的exe文件,用个txt代替后修改权限,从而使病毒不能运行.
前些天,又被这个病毒唤醒了,就在网上搜罗. 从本论坛 下载到一个由 @cdj68765 制作的Synaptics病毒专杀工具,
[PC样本分析] Synaptics蠕虫病毒感染解决方案
[开始还不是本站会员]由于权限所限,只能看到第一页.下载了一个1.1.1.1版本的, 也不晓得是不是最新的文件.
同一时间,搜索到一篇分析报告文章 , 偷梁换柱:谨防“Synaptics”蠕虫病毒
知道了病毒的运作过程.于是用下载的专杀工具,效果感觉很好, U盘空间顿时空了两三百兆出来, 根据工具中扫描时提示的目录信息,我再到U盘的相关目录中去查看,
用exeScope工具来检查一些文件时,发现还是存在感染文件,但是专杀工具识别不了.正如作者所说:
目前已知问题,对某些exe文件会无法恢复,问题发生的原因不明,程序会自动跳过该类文件但是病毒还在要小心
对长路径名的文件无法处理,这个是Win系统的通病,我也不清楚病毒是怎么做到对长路径和长文件名的感染的
被感染的xlsm文件恢复还处于验证阶段,需要你们的测试结果
现在以 病毒样品文件 : 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#] 纯文本查看 复制代码
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# 来验证专杀工具中的代码检测样品病毒文件描述 的返回值:
[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语句位置不当, 会使遍历过程中遇到错误后,未遍历的文件会跳过,原代码:
[C#] 纯文本查看 复制代码
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逻辑:
[C#] 纯文本查看 复制代码
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 ....
可以将里面的二进制数据做为病毒的特征码.
[C#] 纯文本查看 复制代码
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
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:
[C#] 纯文本查看 复制代码
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]{
[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=2]
int
length = endAddress - startAddress + 1;[/size]
[size=2]
[size=2]
byte
[] buffer =
new
byte
[length];[/size]
[size=2]
[size=2]
try
[/size]
[size=2] {[/size]
[size=2]
using
(FileStream fs =
new
FileStream(filePath, FileMode.Open,FileAccess.Read,FileShare.ReadWrite))
[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:
[C#] 纯文本查看 复制代码
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
)
{
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++)
{
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]);
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; }
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 内部
[C#] 纯文本查看 复制代码
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)
{
Console.WriteLine(
"ScanDirectoryRecursively--return"
);
return
;
}
foreach
(
string
file
in
Directory.GetFiles(directory,
"*.exe"
))
{
try
{
scanCount++;
(sender
as
BackgroundWorker).ReportProgress(1,file +
'|'
+ scanCount);
if
(!file.ToLower().EndsWith(
".exe"
))
continue
;
if
(skipCache &&file.Contains(
"\\._cache_"
))
continue
;
long
.TryParse(GetFileSize(file,
false
),
out
longfileSizeB);
if
(fileSizeB < VIR_FILE_MIN_SIZE)
continue
;
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);
dataGridView1.Invoke(
new
Action(()=>
{
dataGridView1.Rows.Insert(0, (dataGridView1.Rows.Count).ToString(), file,Rlt[0]);
}));
string
repairResult =RepairFile(file, isTest);
dataGridView1.Invoke(
new
Action(()=>
{
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))
{
if
(reqStop)
{
Console.WriteLine(
"ScanDirectoryRecursively--dir--return"
);
return
;
}
try
{
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)制作匆忙,其它不足之处,请不吝赐教,后面有时间再做一个完善的版本给需要的人.
6 再次感谢 @cdj68765 前辈提供的源程序,及 pianshen 里的分析报告.
7.其它说明:
本人不是专业程序员,也不从事软件开发的工作,只是对各种事物喜欢研究,但终归是杂而不专,以上内容也是个人兴趣所至,有感而发. 谨以此为媒,加入本论坛.有时间就会上来观摩大神,继续研究.
附一个甩不掉的小尾巴: S y n a p t i c s P o i n t i n g D e v i c e D r i v e r
免费评分
查看全部评分