U盘驱动配置工具开发
Table of Contents
1 背景
手里有一个EAGET的U盘,想做一个类似于某宝上卖的加密U盘,基本原理是使用量产工具将 其格式化为CDROM加U盘公开分区和隐藏分区模式,在CDROM中放加解密工具,U盘插上电脑后 可以运行CDROM中的程序来挂载隐藏分区。
遇到的问题是EAGET的USBTOOLBAR工具是建立在WINDOWS系统设置正常的前提下的,如果系统 USB存储驱动被恶意程序修改,比如加上了过滤驱动,会使程序从U盘上读取到数据不是期望 的数据,导致无法显示隐藏盘。
解决的办法只有一种,就是想方设法地让系统针对我们的U盘使用原来正常的驱动。本文的 重点是如何开发一个小工具,解决USB存储驱动设置被篡改的问题。学习和尝试的过程就不 写了,以下主要记录相关知识的要点。
2 驱动的安装和应用
U盘插入系统后驱动安装和设备启动的过程在Microsoft 文档 Adding a pnp Device to a Running System 一文中讲得非常清楚,大概如下:
- 总线驱动程序发现设备,将通知pnp 管理器
- Pnp 管理器与总线交互收集设备信息,配置设备并将相关信息存入注册表
- 内核模式中的Pnp 管理器与用户模式的pnp 管理器和安装组件协调,查找并安装前端过滤 驱动、功能驱动以及后端过滤驱动
- 分配资源,启动设备
值得注意的是系统为设备匹配驱动的过程,如果U盘在系统上使用过,在注册表中 HKLM\System\CurrentControlSet\Enum\USBSTOR 路径下会有记录,其中包括设备实例所需 的驱动、配置及驱动信息,系统会直接使用这里的设置为U盘匹配驱动,只有当注册表中找 不到U盘实例的信息时才会为其安装驱动。驱动安装的过程在 Microsoft 文档:如何选择驱 动程序中有详细的描述。
而恶意程序修改驱动配置则主要是加入自己写的过滤驱动,其配置位置则在注册表 HKLM\System\CurrentControlSet\Control\Class 下关于USBSTOR Class Guid 项。
3 方案选择
为了不使用系统自动为U盘安装的驱动,则要改变应用于U盘实例的配置及其安装类(错误配 置的位置)。有两种方案,其一是专门针对某U盘设计一个驱动程序,在U盘插入系统后进行 安装,因为驱动的INF文件中可以配置自定义的安装类,即可绕过恶意配置的拦截;其二是 修改当前驱动配置,即前文提到注册表项,直接绕过恶意配置,当然同样需要添加自定义的 CLASS。这里之所以不直接修改{4D36E967-E325-11CE-BFC1-08002BE10318}项的原因是为了 最大限度保持系统稳定,以避免造成其他磁盘不可用。
方案一是基于系统会选择最为匹配的驱动的基础上,方案二则是基于注册表其实是系统查找 驱动的第一位置。
方案一 的优点是通过管理员权限即可安装驱动,缺点是首先要针对自己的U盘准备磁盘驱 动及INF文件,其次是要通过手动更新驱动(不论是通过设备管理器还是通过编写程序), 再次安装设备驱动的过程比较长。此方案经过简单实验论证,参照libusb 项目,应该是可 以成功的。难度不高,主要是INF文件的编写,微软文档INF文件有详细的介绍。
方案二 的优点是速度快,不需要用户过多的操作,缺点是使用程序直接操作注册表关键 位置需要SYSTEM用户权限。此方案通过手动修改注册表进行验证可行。
综合比较,第二案更加适合制作加密U盘的应用场景。
4 方案设计
由于与系统高度相关,选择Windows 编程常用的C/C++语言。据前文所述,难点是提升程 序操作权限。
4.1 提权工作服务程序
由于需要System用户才可以操作相应注册表项,所以需要想法提升程序的权限,参照网上可 查的at 漏洞法、超级Cmd法、Psexe法,在程序中适宜用创建服务的方法来提升权限,基本 原理是创建的系统服务默认是以 System 用户来执行的,查看任务管理器就明白了。
4.2 交互界面程序
如果使用系统服务的方式,与用户的交互就比较弱,为了给用户更好的体验,创建一个对话 框,对话框担负着与用户和服务程序交互任务。
5 服务程序
服务程序主要是为了解决操作注册表的权限限制问题。服务程序有自己特定的要求,主要有 3个函数。
- StartServiceCtrlDispatcher 注册并开始服务
- RegisterServiceCtrlHandler 注册服务的控制方法(回调)
- SetServiceStatus 报告服务状态
5.1 基本写法
void main(int argc, char* argv[])
{
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = "ServiceName";
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
// Start the control dispatcher thread for our service
StartServiceCtrlDispatcher(ServiceTable);
}
void ServiceMain(int argc, char** argv)
{
int error;
ServiceStatus.dwServiceType =
SERVICE_WIN32;
ServiceStatus.dwCurrentState =
SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted =
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
//为服务注册控制方法 ControlHandler
hStatus = RegisterServiceCtrlHandler(
"ServiceName",
(LPHANDLER_FUNCTION)ControlHandler);
if (hStatus == (SERVICE_STATUS_HANDLE)0)
{
// Registering Control Handler failed
return;
}
// 在 InitService 中做一些准备工作,如果不能完成则退出
error = InitService();
if (error)
{
// Initialization failed
ServiceStatus.dwCurrentState =
SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
// 向服务管理器报告服务状态,否则服务管理器会报告错误
ServiceStatus.dwCurrentState =
SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
// The worker loop of a service
while (ServiceStatus.dwCurrentState ==
SERVICE_RUNNING)
{
int result = DoSomething()
if (result)
{
ServiceStatus.dwCurrentState =
SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus,
&ServiceStatus);
return;
}
Sleep(SLEEP_TIME);
}
return;
}
// Control Handler
void ControlHandler(DWORD request)
{
switch(request)
{
case SERVICE_CONTROL_STOP:
OutputDebugString("Monitoring stopped.");
WriteToLog("Monitoring stopped.");
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
case SERVICE_CONTROL_SHUTDOWN:
OutputDebugString("Monitoring stopped.");
WriteToLog("Monitoring stopped.");
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
default:
break;
}
5.2 注册表操作
注册表操作的大体流程是先打用RegOpenKey打开注册表,而后增、删、查、改等操作,最后 RegCloseKey 关闭注册表。
RegOpenKey 打开注册表,第二个参数是要打开的路径,第三个参数是返回的句柄。
lRet = RegOpenKey(HKEY_LOCAL_MACHINE, OLD_CLASS_KEY, &hKey);
RegSaveKey 备份指定项,第二个参数是要保存到磁盘上的路径。
lRet = RegSaveKey(hKey,service_class_back_file_path,NULL);
SHDeleteKey 删除注册表,不同于RegDeleteKey,它可以递归删除所有的子项及键值。但需 要提供不同的头文件。
#include <shlwapi.h> lRet = SHDeleteKey(hKey, NEW_CLASS_GUID);
RegCreateKey 创建项,如果存在则打开它,参数同RegOpenKey
lRet = RegCreateKey(hKey, NEW_CLASS_GUID, &hKeyClass);
RegRestoreKeyA 恢复注册表项,第二个参数是文件路径,第三个参数表示是否强制恢复
lRet = RegRestoreKeyA(hKeyClass,service_class_back_file_path,REG_FORCE_RESTORE);
RegSetValueExA 设置键的值,第一个参数是打开注册表项的句柄,第二个是其下的键名, 第三个未用,第四个表示值的类型,第五个是要设置的值,第六个是指值的长度。这里要注 意的是第四个参数是强制转换为BYTE指针的,第六个参数也是指字节长度。
lRet = RegSetValueExA(hKeyClass, "Class", 0, REG_SZ, (BYTE*)NEW_CLASS_NAME,11);
RegQueryKey 查询键的值,第二个参数是键名,第三个未用,第四个接收值的类型,第五个 接收值,第六个接收值的长度。
lRet = RegQueryValueExA(hKey,"Driver",0,&gv_type,(LPBYTE)old_driver,(LPDWORD)&old_driver_size);
RegCloseKey 关闭句柄。
RegCloseKey(hKey);
在本案例中采取的方法是,先将原Class( {4D36E967-E325-11CE-BFC1-08002BE10318}项 ) 备份出来,而后恢复到新的项上,修改成新Class的名字,恢复过滤驱动设置为系统默认设 置,然后修改注册表中U盘枚举项下我们U盘的设置,使它使用新的Class(即系统默认设置), 以绕过恶意程序对U盘的劫持。
5.3 权限管理
在恢复注册表项时可能还会遇到权限问题,这时需要 EnablePrivilege 和 SetPrivilege 这两个函数来设置权限,他们的定义如下。
int EnablePrivilege(LPCTSTR lpszPrivilege, BOOL bEnable)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ, &hToken))
return 1;
if(!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
return 2;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, NULL, NULL, NULL);
CloseHandle(hToken);
return 0;
}
bool SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
HANDLE hToken = NULL;
TOKEN_PRIVILEGES tp;
LUID luid;
if(OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) == 0)
{
return false;
}
if (!LookupPrivilegeValueA(NULL, lpszPrivilege, &luid))
{
return false;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if(bEnablePrivilege)
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else
{
tp.Privileges[0].Attributes = 0;
}
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
{
return false;
}
return 0;
}
5.4 管道通信
管道通信其实是服务端与客户端的通信,读取端作为服务端建立管道,等候连接,待客户端 连接上管道并写入数据即可读出。
#define MY_STATUS_PIPE "\\\\.\\Pipe\\mydisk_status"
#define MY_CMD_PIPE "\\\\.\\Pipe\\mydisk_cmd"
//创建状态管道等候连接
hPipeCmd = CreateNamedPipe(MY_CMD_PIPE,PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT,PIPE_UNLIMITED_INSTANCES,0,0,NMPWAIT_WAIT_FOREVER,0);
my_message * ReadCmd()
{
//等待客户端连接
if(ConnectNamedPipe(hPipeCmd,NULL)==NULL)
{
//如果上次连接没有使用 DisconnectNamedPipe 断开,则会出现此错误
if(GetLastError()!=ERROR_PIPE_CONNECTED)
{
WriteToLog("Waiting user to connect command pipe error.\n");
return FALSE;
}
}
my_message * cmd = (my_message *)malloc(sizeof(my_message));
//注意要在适当的地方释放内存
if(cmd==NULL)return NULL;
BOOL fSuccess = false;
DWORD len=0;
LPBYTE p = (LPBYTE)cmd;
while(TRUE)
{
fSuccess = ReadFile(hPipeCmd,p,sizeof(my_message)-(p- (LPBYTE)cmd),&len,NULL);
if(!fSuccess || p+len>= (LPBYTE)cmd+sizeof(my_message))break;
p = p +len;
}
//断开连接
DisconnectNamedPipe(hPipeCmd);
if(fSuccess)
{
return cmd;
}
return NULL;
}
在客户端(GUI)中,采取如下方式写入命令,并等待读取完成状态。
BOOL service_exec_cmd(LONG cmd,HWND hwndDlg,LPCSTR device)
{
if(WaitNamedPipe(MY_CMD_PIPE,NMPWAIT_WAIT_FOREVER)==FALSE)
{
MessageBox(hwndDlg,"Maybe the service is not ready. You can restart this application or try again.","ERROR",MB_OK|MB_ICONERROR);
return FALSE;
}
hPipeCmd = CreateFile(MY_CMD_PIPE,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hPipeCmd == INVALID_HANDLE_VALUE)
{
MessageBox(hwndDlg,"Open command pipe failed.","ERROR",MB_OK|MB_ICONERROR);
return FALSE;
}
DWORD dwWrite;
my_message *msg =( my_message *) malloc(sizeof(my_message));
memset(msg,'\0',sizeof(my_message));
msg->p1=cmd;
strcpy(msg->p2,device);
if(!WriteFile(hPipeCmd,(LPBYTE)msg,sizeof(my_message),&dwWrite,NULL))
{
MessageBox(hwndDlg,"Send command to service failed.","ERROR",MB_OK|MB_ICONERROR);
free(msg);
msg = NULL;
return FALSE;
}
//重置为0用来接收响应
memset(msg,'\0',sizeof(my_message));
//读取工作响应
if(ConnectNamedPipe(hPipeStatus,NULL)==NULL)
{
if(GetLastError()!=ERROR_PIPE_CONNECTED)
{
MessageBox(hwndDlg,"Waiting service connect to status pipe error.","ERROR",MB_OK|MB_ICONERROR);
free(msg);
msg=NULL;
return FALSE;
}
}
BOOL fSuccess = false;
DWORD len=0;
LPBYTE lpMsg = (LPBYTE)msg;
LPBYTE p = lpMsg;
char log_buffer[MAX_PATH];
while(TRUE)
{
fSuccess = ReadFile(hPipeStatus,p,sizeof(my_message)-(p-lpMsg),&len,NULL);
sprintf(log_buffer,"%d",GetLastError());
if(!fSuccess)MessageBox(hwndDlg,log_buffer,"ERROR",MB_OK|MB_ICONERROR);
if(!fSuccess || p+len>=lpMsg+sizeof(my_message))break;
p = p +len;
}
DisconnectNamedPipe(hPipeStatus);
if(fSuccess && msg->p1 ==ERROR_SUCCESS)
{
if(msg!=NULL)free(msg);
return TRUE;
}
if(fSuccess && cmd!=ID_GET_INFO)MessageBox(hwndDlg,msg->p2,"ERROR",MB_OK|MB_ICONERROR);
if(msg!=NULL)free(msg);
return FALSE;
}
关于 my_message,这是自定义的结构体,定义如下:
typedef struct _my_message{
LONG p1;
char p2[MAX_PATH];
} my_message;
因为系统不保证结构体内存空间的连续性,所以采取动态分配空间的方式,保证在写入管道 的时候是连续的内存块。
6 界面程序
界面主要为用户提供直观的交互接口,不需要太复杂,需要一些按钮来发送命令,需要一些 简单信息的显示。
6.1 文件检查
6.2 创建与删除服务
bool_t usb_service_create(const char *name, const char *display_name,
const char *binary_path, unsigned long type,
unsigned long start_type)
{
SC_HANDLE scm = NULL;
SC_HANDLE service = NULL;
bool_t ret = FALSE;
if (!usb_service_load_dll())
{
return FALSE;
}
do
{
scm = open_sc_manager(NULL, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_ALL_ACCESS);
if (!scm)
{
USBERR("opening service control "
"manager failed: %s", usb_win_error_to_string());
break;
}
service = open_service(scm, name, SERVICE_ALL_ACCESS);
if (service)
{
if (!change_service_config(service,
type,
start_type,
SERVICE_ERROR_NORMAL,
binary_path,
NULL,
NULL,
NULL,
NULL,
NULL,
display_name))
{
USBERR("changing config of "
"service '%s' failed: %s",
name, usb_win_error_to_string());
break;
}
ret = TRUE;
break;
}
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
{
service = create_service(scm,
name,
display_name,
GENERIC_EXECUTE,
type,
start_type,
SERVICE_ERROR_NORMAL,
binary_path,
NULL, NULL, NULL, NULL, NULL);
if (!service)
{
USBERR("creating "
"service '%s' failed: %s",
name, usb_win_error_to_string());
}
ret = TRUE;
}
}
while (0);
if (service)
{
start_service(service,0,NULL);//创建以后则打开
close_service_handle(service);
}
if (scm)
{
close_service_handle(scm);
}
usb_service_free_dll();
return ret;
}
这一段代码来自于 libusb-win32 ,这是一个值得一读的项目,删除服务的方法在其中也有。
6.3 对话框
创建一个对话框也是很简单了,就一句话。第一个参数来自于WinMain 的第一个参数,第二 个参数是使用 MAKEINTRESOURCE 宏对 DLG_MAIN * 资源 * 的处理,第三个参数是一个消 息处理回调函数。
return DialogBox(hInst,MAKEINTRESOURCE(DLG_MAIN),NULL,(DLGPROC)DialogProc);
6.4 消息处理
先说消息处理函数 DialogProc,就像下面这个样子,WM_INITDIALOG,WM_CLOSE的意思就一 目了然了,前者是在对话框初始化,后者是对话框关闭时收到的消息,而 WM_COMMAND 则表 示用户点击按钮发送了某个消息,具体是哪个按钮呢,从wParam 参数可以找到。
BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
}
return TRUE;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case ID_MOUNT_PUBLIC:
case ID_MOUNT_HIDE:
case ID_MOUNT_SECRET:
case ID_RESET_CONFIG:
case ID_DELETE_USBSTOR_ENUM:
if(strlen(device_id)<=0)
{
MessageBox(hwndDlg,"You must select one device.","ERROR",MB_OK|MB_ICONERROR);
return TRUE;
}
if(!PostThreadMessage(nThreadID,LOWORD(wParam),(WPARAM)device_id,0))
{
char log_buffer[MAX_PATH];
sprintf(log_buffer,"Send command to working thread failed.[%d]",GetLastError());
MessageBox(hwndDlg,log_buffer,"ERROR",MB_OK|MB_ICONERROR);
}
break;
case ID_EXIT:
PostThreadMessage(nThreadID,ID_EXIT,NULL,0);
PostMessage(hwndDlg,WM_CLOSE,NULL,NULL);
break;
case ID_FRESH_DEVICE_LIST:
FreshDeviceList(dev_cmbox);
break;
}
}
return TRUE;
}
return FALSE;
}
此例中,wParam 是一个数字,被按下的按钮的ID 号,按钮是怎么定义和运用的呢,请看下 一节 。
6.5 资源
我的工程中有一个文件 resource.h,定义了对话框和按钮的ID号,其中的内容大致如下:
#ifndef IDC_STATIC #define IDC_STATIC (-1) #endif #define DLG_MAIN 100 #define ID_MOUNT_PUBLIC WM_USER+1 #define ID_MOUNT_HIDE WM_USER+2 #define ID_MOUNT_SECRET WM_USER+3 #define ID_RESET_CONFIG WM_USER+4 #define ID_EXIT WM_USER+5 #define ID_GET_INFO WM_USER+5 #define ID_DELETE_USBSTOR_ENUM WM_USER+7 #define ID_FRESH_DEVICE_LIST WM_USER+8
这里要注意的是ID号最好不要乱定义,否则在执行过程中可能存在莫名其妙的错误,一般是 在WM_USER段以上。
而资源文件则是 resource.rc,是关于对话框、图标等具体定义,微软文档中资源定义语法 有详细介绍。下面只是一个例子。
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"
MYICON ICON "favicon.ico"
//
// Dialog resources
//
LANGUAGE 0, SUBLANG_NEUTRAL
DLG_MAIN DIALOG 0, 0, 210, 150
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "MyApp"
FONT 10, "Ms Shell Dlg"
{
ICON MYICON,106,10,125
COMBOBOX IDI_DEVICE,55,5,150,20,WS_VSCROLL|WS_TABSTOP|CBS_DROPDOWNLIST
DEFPUSHBUTTON "刷新设备",ID_FRESH_DEVICE_LIST,55,25,70,20
DEFPUSHBUTTON "挂载分区",ID_MOUNT_PUBLIC,135,25,70,20
DEFPUSHBUTTON "挂载隐藏区",ID_MOUNT_HIDE,135,45,70,20
DEFPUSHBUTTON "挂载加密区",ID_MOUNT_SECRET,135,65,70,20
DEFPUSHBUTTON "重置驱动",ID_RESET_CONFIG,135,85,70,20
DEFPUSHBUTTON "清除U盘记录",ID_DELETE_USBSTOR_ENUM,135,105,70,20
DEFPUSHBUTTON "退出",ID_EXIT,135,125,70,20
LTEXT "选择U盘",IDC_STATIC,5,5,50,20
LTEXT "当前系统",IDC_STATIC,5,50,50,20
LTEXT "",IDT_SYSTEM,55,50,70,20,WS_BORDER
LTEXT "配置结果",IDC_STATIC,5,70,50,20
LTEXT "",IDT_DRIVER,55,70,70,20,WS_BORDER
LTEXT "程序版本",IDC_STATIC,5,90,50,20
LTEXT "Version 1.0",IDC_STATIC,55,90,70,20,WS_BORDER
LTEXT "解决驱动配置错误引起的U盘读写问题。",IDC_STATIC,55,120,70,30
}
//
// Manifest resources
//
LANGUAGE 0, SUBLANG_NEUTRAL
1 RT_MANIFEST ".\\manifest.xml"
6.6 线程
我们的应用中涉及到使用管道进行通信,存在阻塞的可能情况,为了给用户较好的操作体验, 需要把这种费时的读写操作分离出来,使用单独线程进行处理,主线程与子线程之间采取消 息进行通信。
HANDLE hStartEvent;
HANDLE hThread;
unsigned nThreadID;
//创建线程
hThread = (HANDLE)_beginthreadex(NULL,0,ProcessThread,(LPVOID)hwndDlg,0,&nThreadID);
if(hThread==0)
{
MessageBox(hwndDlg,"Create working thread failed.","ERROR",MB_OK|MB_ICONERROR);
CloseHandle(hStartEvent);
return FALSE;
}
WaitForSingleObject(hStartEvent,INFINITE);
CloseHandle(hStartEvent);
线程的定义。
unsigned __stdcall ProcessThread(LPVOID lpParameter)
{
MSG msg;
//查看有无消息,这里主要是保证消息队列被创建
PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_NOREMOVE);
if(!SetEvent(hStartEvent))
{
return 1;
}
char file_path[MAX_PATH];
HWND hwndDlg = (HWND)(lpParameter);
while(TRUE)
{
//获取消息
if(GetMessage(&msg,0,0,0))
{
char * device = (char *)msg.wParam;
switch(msg.message)
{
case ID_MOUNT_PUBLIC:
break;
case ID_EXIT:
return 0;
case ID_MOUNT_HIDE:
break;
case ID_MOUNT_SECRET:
break;
case ID_RESET_CONFIG:
break;
case ID_DELETE_USBSTOR_ENUM:
break;
}
}
}
}
消息的发送。
PostThreadMessage(nThreadID,LOWORD(wParam),(WPARAM)device_id,0);
关于PeekMessage、GetMessage 和 PostThreadMessage 的介绍可以从这里了解到。
7 程序编译
如果使用CodeBlocks,在创建工程的时候,服务程序可以选择 win32 console 程序,而界 面程序要选择 win32 Gui 程序,这会减少一些麻烦,因为IDE 为你设置好了编译和连接选 项,当然你也可以采取手动方式在设置工程的编译和连接选项。有时这还是必须的,比如提 醒undefined reference to xxxx 的时候,就需要添加一些连接库,比如 setupapi 等等。
8 后记
使用 UltraISO 将开发的驱动配(重)置工具打包到ISO镜像中,再使用量产工具重新量产U 盘为USB CDROM+USB DISK,驱动配(重)置工具位于CD中,U盘插入系统后,运行程序重置驱动, 成功解决U盘驱动被劫持的问题。当然,对于USB CDROM也被禁用的情况,此法就不可用了。
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议 进行许可。
posted on 2022-09-09 10:41 YourTech-WuPeng 阅读(561) 评论(1) 收藏 举报
浙公网安备 33010602011771号