狸猫换太子:动态替换WinCE的原生驱动!
//=====================================================================
//TITLE:
//??? 貍貓換太子:動態替換WinCE的原生驅動!
//AUTHOR:
//??? norains
//DATE:
//??? Friday 23-April-2010
//Environment:
//??? Windows CE 5.0 + TCC7901
//=====================================================================
?
??? 大家應該都知道,WinCE系統的驅動是可以非常方便地動態加載和卸載的(如果對此不清楚,可以參考我之前寫的兩篇文章。《WinCE驅動的動態加載》:http://blog.csdn.net/norains/archive/2010/02/22/5316923.aspx《WinCE驅動的動態卸載》:http://blog.csdn.net/norains/archive/2010/04/22/5514351.aspx),如果我們善加利用,完全可以神不知鬼不覺地進行貍貓換太子--不必重新編譯系統,就可以在應用程序完全不知情地情況下,將原生驅動偷梁換柱成我們自己的冒牌貨!
?
??? 聽起來很有意思,是吧?那么,讓我們開始這次奇妙的旅程吧。
?
??? 能達到的目標才有動力,這次我們拿WinCE設備最常用的最基本的串口驅動開刀。我們要做的事情也很簡單,如果通過Read讀取回來的字符超過五個,那么我們就將最前面的五個字符替換為"abcde"。
?
??? 如果要達成這個目標,我們需要兩個程序:
??? 1. 偽驅動DLL。它負責替換讀取回來的數據
??? 2. 驅動加載程序。
?
??? 第二點比較簡單,我們先來看看第一點。
????首先我們來了解一下WinCE驅動的基本情況。WinCE的驅動,說白了,就是一個dll文件。它和普通的dll唯一不同的是,它規定了導出函數的形式,比如XXX_Init,XXX_Deinit等等。其中的XXX,是不同驅動的前綴,就像串口驅動為COM_Init一樣。更為嚴格的是,像COM_Init這樣的函數,無論是返回值,還是形參的類型乃至于個數,都是規定死的,否則WinCE不會正常加載。
? ??而在這一框框條條之下,帶給我們的是另外的一種機會:我們的偽驅動向上實現微軟規定的接口,向下則調用原生的驅動。
聽起來是不是有點迷糊?沒關系,我們以COM_Read函數舉個例子。
? 我們的偽驅動名為FakeDriver.dll,導出了一個函數叫COM_Read。我這里的平臺是Telechips的TCC7901,它的驅動文件為tcc_serial.dll。接下來需要做的是,從tcc_serial.dll中獲得其COM_Read的地址,以方便我們在偽驅動中調用。
對于大家來說,對于下面調用dll函數的代碼應該不會陌生:
//顯式加載DLL文件 g_hCoreDll = LoadLibrary(TEXT("tcc_serial.dll")); ... //獲取DLL文件的COM_Read函數地址 typedef ULONG (WINAPI *DLL_COM_READ)(HANDLE pHead,PUCHAR pTargetBuffer,ULONG BufferLength); DLL_COM_READ DLL_COM_Read = (DLL_COM_READ)GetProcAddress(g_hCoreDll, _T("COM_Read")); ... ULONG COM_Read(HANDLE pHead,PUCHAR pTargetBuffer,ULONG BufferLength) { //直接調用DLL的COM_Read函數 return DLL_COM_Read(pHead,pTargetBuffer,BufferLength); }
沒錯,就是這么簡單。我們的偽驅動,其實只是在原生驅動之上封裝了一層,骨子里還是調用的原生驅動。只不過我們可以在這封裝的一層中,可以做太多的事情了。對于本文前面的目標,我們的偽驅動的代碼完整實現如下:
#include "serpriv.h" //------------------------------------------------------------ //導出函數的定義 typedef HANDLE (WINAPI *DLL_COM_INIT)(ULONG Identifier); typedef BOOL (WINAPI *DLL_COM_DEINIT)(PHW_INDEP_INFO pSerialHead); typedef BOOL (WINAPI *DLL_COM_IOCONTROL)(PHW_OPEN_INFO pOpenHead,DWORD dwCode, PBYTE pBufIn,DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,PDWORD pdwActualOut); typedef void (WINAPI *DLL_COM_PRECLOSE)(PHW_OPEN_INFO pOpenHead); typedef BOOL (WINAPI *DLL_COM_PREDEINIT)(PHW_INDEP_INFO pSerialHead); typedef HANDLE (WINAPI *DLL_COM_OPEN)(HANDLE pHead, DWORD AccessCode,DWORD ShareMode); typedef BOOL (WINAPI *DLL_COM_CLOSE)(PHW_OPEN_INFO pOpenHead); typedef BOOL (WINAPI *DLL_COM_POWERDOWN)(HANDLE pHead); typedef BOOL (WINAPI *DLL_COM_POWERUP)(HANDLE pHead); typedef ULONG (WINAPI *DLL_COM_READ)(HANDLE pHead,PUCHAR pTargetBuffer,ULONG BufferLength); typedef ULONG (WINAPI *DLL_COM_SEEK)(HANDLE pHead,LONG Position,DWORD Type); typedef ULONG (WINAPI *DLL_COM_WRITE)(HANDLE pHead,PUCHAR pSourceBytes,ULONG NumberOfBytes); //---------------------------------------------------------- //全局變量 DLL_COM_INIT DLL_COM_Init = NULL; DLL_COM_DEINIT DLL_COM_Deinit = NULL; DLL_COM_IOCONTROL DLL_COM_IOControl = NULL; DLL_COM_PRECLOSE DLL_COM_PreClose = NULL; DLL_COM_PREDEINIT DLL_COM_PreDeinit = NULL; DLL_COM_OPEN DLL_COM_Open = NULL; DLL_COM_CLOSE DLL_COM_Close = NULL; DLL_COM_POWERDOWN DLL_COM_PowerDown = NULL; DLL_COM_POWERUP DLL_COM_PowerUp = NULL; DLL_COM_READ DLL_COM_Read = NULL; DLL_COM_SEEK DLL_COM_Seek = NULL; DLL_COM_WRITE DLL_COM_Write = NULL; HINSTANCE g_hCoreDll = NULL; //---------------------------------------------------------- //獲取所有導出函數的地址 BOOL InitFuncAddr() { BOOL bRes = FALSE; __try { g_hCoreDll = LoadLibrary(TEXT("tcc_serial.dll")); if (g_hCoreDll == NULL) { __leave; } DLL_COM_Init = (DLL_COM_INIT)GetProcAddress(g_hCoreDll, _T("COM_Init")); if(DLL_COM_Init == NULL) { __leave; } DLL_COM_Deinit = (DLL_COM_DEINIT)GetProcAddress(g_hCoreDll, _T("COM_Deinit")); if(DLL_COM_Deinit == NULL) { __leave; } DLL_COM_IOControl = (DLL_COM_IOCONTROL)GetProcAddress(g_hCoreDll, _T("COM_IOControl")); if(DLL_COM_IOControl == NULL) { __leave; } DLL_COM_PreClose = (DLL_COM_PRECLOSE)GetProcAddress(g_hCoreDll, _T("COM_PreClose")); if(DLL_COM_PreClose == NULL) { __leave; } DLL_COM_PreDeinit = (DLL_COM_PREDEINIT)GetProcAddress(g_hCoreDll, _T("COM_PreDeinit")); if(DLL_COM_PreDeinit == NULL) { __leave; } DLL_COM_Open = (DLL_COM_OPEN)GetProcAddress(g_hCoreDll, _T("COM_Open")); if(DLL_COM_Open == NULL) { __leave; } DLL_COM_Close = (DLL_COM_CLOSE)GetProcAddress(g_hCoreDll, _T("COM_Close")); if(DLL_COM_Close == NULL) { __leave; } DLL_COM_PowerDown = (DLL_COM_POWERDOWN)GetProcAddress(g_hCoreDll, _T("COM_PowerDown")); if(DLL_COM_PowerDown == NULL) { __leave; } DLL_COM_PowerUp = (DLL_COM_POWERUP)GetProcAddress(g_hCoreDll, _T("COM_PowerUp")); if(DLL_COM_PowerUp == NULL) { __leave; } DLL_COM_Read = (DLL_COM_READ)GetProcAddress(g_hCoreDll, _T("COM_Read")); if(DLL_COM_Read == NULL) { __leave; } DLL_COM_Seek = (DLL_COM_SEEK)GetProcAddress(g_hCoreDll, _T("COM_Seek")); if(DLL_COM_Seek == NULL) { __leave; } DLL_COM_Write = (DLL_COM_WRITE)GetProcAddress(g_hCoreDll, _T("COM_Write")); if(DLL_COM_Write == NULL) { __leave; } bRes = TRUE; } __finally { if(bRes == FALSE) { if(g_hCoreDll != NULL) { FreeLibrary(g_hCoreDll); } } } return bRes; } BOOL WINAPI DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved) { switch ( dwReason ) { case DLL_PROCESS_ATTACH: break; } return TRUE; } HANDLE COM_Init(ULONG Identifier) { if(InitFuncAddr() != FALSE) { RETAILMSG(TRUE,(TEXT("InitFuncAddr Succeeded/r/n"))); return DLL_COM_Init(Identifier); //return TRUE; } else { RETAILMSG(TRUE,(TEXT("InitFuncAddr Failed/r/n"))); return FALSE; } } BOOL COM_Deinit(PHW_INDEP_INFO pSerialHead) { BOOL bRes = DLL_COM_Deinit(pSerialHead); if(g_hCoreDll != NULL) { FreeLibrary(g_hCoreDll); } RETAILMSG(TRUE,(TEXT("COM_Deinit/r/n"))); return bRes; } BOOL COM_IOControl(PHW_OPEN_INFO pOpenHead, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut) { return DLL_COM_IOControl(pOpenHead,dwCode,pBufIn,dwLenIn,pBufOut,dwLenOut,pdwActualOut); } void COM_PreClose(PHW_OPEN_INFO pOpenHead) { DLL_COM_PreClose(pOpenHead); } BOOL COM_PreDeinit(PHW_INDEP_INFO pSerialHead) { return DLL_COM_PreDeinit(pSerialHead); } HANDLE COM_Open(HANDLE pHead, DWORD AccessCode,DWORD ShareMode) { return DLL_COM_Open(pHead,AccessCode,ShareMode); } BOOL COM_Close(PHW_OPEN_INFO pOpenHead) { return DLL_COM_Close(pOpenHead); } BOOL COM_PowerDown(HANDLE pHead) { return DLL_COM_PowerDown(pHead); } BOOL COM_PowerUp(HANDLE pHead) { return DLL_COM_PowerUp(pHead); } ULONG COM_Read(HANDLE pHead,PUCHAR pTargetBuffer,ULONG BufferLength) { ULONG ulRead = DLL_COM_Read(pHead,pTargetBuffer,BufferLength); //將前面的五個字符替換為abced if(ulRead > 5) { pTargetBuffer[0] = 'a'; pTargetBuffer[1] = 'b'; pTargetBuffer[2] = 'c'; pTargetBuffer[3] = 'd'; pTargetBuffer[4] = 'e'; } return ulRead; } ULONG COM_Seek(HANDLE pHead,LONG Position,DWORD Type) { return DLL_COM_Seek(pHead,Position,Type); } ULONG COM_Write(HANDLE pHead,PUCHAR pSourceBytes,ULONG NumberOfBytes) { return DLL_COM_Write(pHead,pSourceBytes,NumberOfBytes); }
代碼中的DLL_COM_XXX函數其實全部都是原生驅動的導出函數,如果我們將COM_Read的替換代碼注釋掉,那么這個偽驅動的表現將和原生驅動一模一樣,沒有任何區別。
偽驅動DLL已經完成,剩下的就是臨門一腳。先看看TCC7901的COM1的注冊表:
如圖中所示,為了要讓我們的偽驅動能夠正常加載,需要將注冊表的dll字段更改為我們偽驅動的文件名。
所以,加載的應用程序就只有如下幾行(代碼中所提到的Unload函數,請參考本文開頭的兩篇文章):
//卸載原來的COM1驅動 Unload(TEXT("COM1:")); //更改注冊表中DLL的文件名 CReg reg; reg.Create(HKEY_LOCAL_MACHINE,TEXT("Drivers//BuiltIn//Serial1")); reg.SetSZ(TEXT("Dll"),TEXT("FakeDriver.dll")); //動態加載偽驅動 ActivateDeviceEx(TEXT("Drivers//BuiltIn//Serial1",NULL,0,pParam);
最后我們需要做的就是,將偽驅動FakeDriver.dll拷貝到windows文件夾,運行我們的加載程序,就可以用如下代碼進行測試:
HANDLE hCom = CreateFile(TEXT("COM1:"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); std::vector<BYTE> vtBuf(MAX_PATH,0); DWORD dwRead = 0; BOOL bRes = ReadFile(hCom,&vtBuf[0],vtBuf.size(),&dwRead,NULL); CloseHandle(hCom);
如果不出意外的話,那么讀取回來的數據前面五個字符將是我們所期望的abcde!
本文所提到的偽驅動源代碼可于此下載:http://download.csdn.net/source/2275520
轉載于:https://www.cnblogs.com/wodeyitian/archive/2010/04/23/2460334.html
總結
以上是生活随笔為你收集整理的狸猫换太子:动态替换WinCE的原生驱动!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.net Table 控件
- 下一篇: 杭电1061题