一种User Mode下访问物理内存及Kernel Space的简单实现
一種User Mode下訪問物理內(nèi)存及Kernel Space的實現(xiàn)
一.背景
?????? WinCE發(fā)展到6.0之后,內(nèi)存結(jié)構(gòu)和管理方法進行了完善。對應(yīng)用程序影響比較大的有Virtual Memory Layout的變化,如每個進程的虛擬內(nèi)存空間擴展為2GB。對驅(qū)動程序影響比較大的有Pointer和Share Memory,這一點在Driver與OS接口部分對指針和內(nèi)存的保護方法中可以看到。對OAL影響比較大的有,系統(tǒng)Boot Process的改變。
?????? 另外,6.0上廢除了Full Kernel Mode的不合理設(shè)計,將Kernel Mode和User Mode進行了細分。由此帶來了本文中要討論的問題,就是如何在User Mode下操作Kernel Mode下的內(nèi)存空間。
?????? 大家一定想到了,簡單的在User Mode下訪問Kernel Mode下才有權(quán)限訪問的內(nèi)存肯定是行不通,可以間接的通過Kernel下的Driver或者其它與Kernel中的代碼進行通信的方法來訪問。
?????? 本文先討論一下第一種方法實現(xiàn)中要解決的問題,最后會將主要代碼實現(xiàn)粘貼出來。
二.需要解決的幾個問題
?????? 前面已經(jīng)提到主要的實現(xiàn)思路,需要解決的問題是如何加載該驅(qū)動,以及如何保證Driver運行在Kernel Mode下,以及如何將Driver和Exe組合到一起。
1.用戶模式下Driver的加載
?????? 為了方便實現(xiàn)User Mode下Driver的動態(tài)加載,將這支在User Mode和Kernel Mode下做轉(zhuǎn)換的Driver做成流驅(qū)動。
?????? User Mode下加載Driver,只需兩個步驟:首先將Driver拷貝到對象存儲下的Windows目錄下,然后調(diào)用Device Manager的API ActivateDevice()來實現(xiàn)動態(tài)的加載。
?????? 函數(shù)ActivateDevice()的使用非常簡單,其聲明如下:
| This function loads a device driver. For additional functionality, use the ActivateDeviceEx function. HANDLE ActivateDevice( ? LPCWSTR lpszDevKey, ??DWORD dwClientInfo );ParameterslpszDevKey [in] Pointer to a string that identifies the location under the HKEY_LOCAL_MACHINE registry subtree where the Driver registry subkey for the device resides. A driver registry subkey contains the dynamic-link library (DLL) name, device prefix, friendly name, and other device information. dwClientInfo [in] Data to store in the Active registry subkey for the device in the ClientInfo registry entry. The registry path to the Active registry subkey for the device is passed in as the context parameter to the device's XXX_Init (Device Manager) function. After the value in dwClientInfo is stored in the registry under HKEY_LOCAL_MACHINE/Drivers/Active, the Device Manager calls XXX_Init. Devload.h defines DEVLOAD_CLIENTINFO_VALNAME and DEVLOAD_CLIENTINFO_VALTYPE to facilitate access to the ClientInfo key. |
?????? 可以看到,第一個參數(shù)用來指定Driver的注冊表路徑,而第二個參數(shù)用來寫入到Active Key下,如果不需要寫入的話,可以置為NULL。?
2.如何保證加載的Driver處于Kernel Mode下
?????? 6.0下引入了Group的概念,通過注冊表可以去定義一個Group,一個簡單的Group定義如下:
| [HKEY_LOCAL_MACHINE/Drivers/ProcGroup_0002] ??? "ProcName"="servicesd.exe" ??? "ProcVolPrefix"="$services" ??? "ProcTimeout"=dword:20000 ? [HKEY_LOCAL_MACHINE/Drivers/ProcGroup_0003] ??? "ProcName"="udevice.exe" ??? "ProcVolPrefix"="$udevice" |
?????? 其實,簡單點理解Group就是將Driver的加載方式進行細分,方便不同的Driver使用不同的系統(tǒng)組件進行加載。
?????? Driver的注冊表項可以用來指定加載自己的Group,如下:
| [HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Ethman] "Prefix"="ETM" "Dll"="ethman.dll" "Index"=dword:1 ; WZCSVC must be started before ethman "Order"=dword:2A ; Flags==12 is DEVFLAGS_LOADLIBRARY and DEVFLAGS_LOAD_AS_USERPROC "Flags"=dword:12 "UserProcGroup"=dword:3 ; // default to group 3 ? [HKEY_LOCAL_MACHINE/Drivers/BuiltIn/SIP] "Prefix"="SIP" "Dll"="softkb.DLL" "Order"=dword:1 "Index"=dword:0 ;Flags==10 is DEVFLAGS_LOAD_AS_USERPROC "Flags"=dword:10 "UserProcGroup"=dword:3 ; // default to group 3 |
?????? 對于指定通過某個Group進行加載的Driver,系統(tǒng)中進行加載的時候會引入Reflect機制。該機制主要用來對檢察注冊表項中的IoLen和IoBase值,當(dāng)檢察到Driver中訪問了不在IoLen和IoBase指定區(qū)域的物理內(nèi)存時,將會出現(xiàn)系統(tǒng)異常。
?????? 對于那些沒有指定使用Group進行加載的驅(qū)動,WinCE6.0中將其加載到Kernel Mode下。也就是說,它們具有了訪問整個4GB空間的權(quán)限。
?????? 要保證User Mode下加載的Driver處于Kernel Mode,只需要在注冊表中不去指定User Group就可以了。????
3.如何將Driver DLL和Exe 做成一個文件
?????? 之所以將Driver和EXE組合成一個文件,是為了用戶使用的方便。想象一下,如果不把兩者做成一個文件的話,一個簡單的訪問物理內(nèi)存的應(yīng)用程序就變成了兩個文件,那是多么不美觀的事情。
?????? 其實實現(xiàn)將Driver DLL和EXE做成一個文件有兩個方法。方法一,由于DLL和EXE都是PE結(jié)構(gòu)的,可以使用網(wǎng)上的加殼工具將其組合成一個PE文件,而在運行的時候自動去殼即可。方法二,將DLL中的信息提取出來放到EXE的Data Section,然后在運行的時候,將這些數(shù)據(jù)重新組合成一個DLL。
?????? 這里我采用了第二種方法來實現(xiàn)組合Driver和EXE文件。
三.代碼實現(xiàn)
1.將Driver注冊表的操作簡化
?????? 操作過CE下注冊表的兄弟們都知道,微軟設(shè)計的注冊表非常簡單,可是操作API實在是不那么友好。
?????? 我這里使用了PB6.0源文件PUBLIC/WCESHELLFE/OAK/CTLPNL/CPLMAIN/cplmacro.h中的類CReg來實現(xiàn)對Driver注冊表項的讀寫動作。
?????? 該注冊表類主要封裝了注冊表的Open/Read/Write API,為用戶提供了一種更加友好的注冊表操作接口。
?????? 該類的定義和實現(xiàn)如下:
| class CReg { private: ???? HKEY m_hKey; ???? int????? m_Index; ???? LPBYTE?? m_lpbValue; // last value read, if any ? public: ???? BOOL Create(HKEY hkRoot, LPCTSTR pszKey) { ???????? DWORD dwDisp; ???????? return ERROR_SUCCESS==RegCreateKeyEx(hkRoot, pszKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &m_hKey, &dwDisp); ???? } ? ???? BOOL Open(HKEY hkRoot, LPCTSTR pszKey, REGSAM sam=KEY_READ) { ???????? return ERROR_SUCCESS==RegOpenKeyEx(hkRoot, pszKey, 0, sam, &m_hKey); ???? } ? ???? CReg(HKEY hkRoot, LPCTSTR pszKey) { ???????? m_hKey = NULL; ???????? m_Index = 0; ???????? m_lpbValue = NULL; ???????? Open(hkRoot, pszKey); ???? } ? ???? CReg() { ???????? m_hKey = NULL; ???????? m_Index = 0; ???????? m_lpbValue = NULL; ???? } ? ???? ~CReg() { ???????? if(m_hKey) RegCloseKey(m_hKey); ???????? MyFree(m_lpbValue); ???? } ? ???? void Reset() { ???????? if(m_hKey) RegCloseKey(m_hKey); ???????? MyFree(m_lpbValue); ???????? m_hKey = NULL; ???????? m_Index = 0; ???????? m_lpbValue = NULL; ???? } ? ???? operator HKEY() { return m_hKey; } ? ???? BOOL IsOK(void) { return m_hKey!=NULL; } ? ? ???? BOOL EnumKey(LPTSTR psz, DWORD dwLen) { ???????? if(!m_hKey) return FALSE; ???????? return ERROR_SUCCESS==RegEnumKeyEx(m_hKey, m_Index++, psz, &dwLen, NULL, NULL, NULL, NULL); ???? } ? ???? BOOL EnumValue(LPTSTR pszName, DWORD dwLenName, LPTSTR pszValue, DWORD dwLenValue) { ???????? DWORD dwType; ???????? if(!m_hKey) return FALSE; ???????? dwLenValue *= sizeof(TCHAR); // convert length in chars to bytes ???????? return ERROR_SUCCESS==RegEnumValue(m_hKey, m_Index++, pszName, &dwLenName, NULL, &dwType, (LPBYTE)pszValue, &dwLenValue); ???? } ? ???? BOOL ValueSZ(LPCTSTR szName, LPTSTR szValue, DWORD dwLen) { ???????? if(!m_hKey) return FALSE; ???????? dwLen *= sizeof(TCHAR); // convert length in chars to bytes ???????? return ERROR_SUCCESS==RegQueryValueEx(m_hKey, szName, NULL, NULL, (LPBYTE)szValue, &dwLen); ???? } ? ???? DWORD ValueBinary(LPCTSTR szName, LPBYTE lpbValue, DWORD dwLen) { ???????? if(!m_hKey) return FALSE; ???????? DWORD dwLenWant = dwLen; ???????? if(ERROR_SUCCESS==RegQueryValueEx(m_hKey, szName, NULL, NULL, lpbValue, &dwLen)) ????????????? return dwLen; ???????? else ????????????? return 0; ???? } ? ???? LPCTSTR ValueSZ(LPCTSTR szName); ? ???? LPBYTE ValueBinary(LPCTSTR szName) { ???????? return (LPBYTE)ValueSZ(szName); ???? } ? ???? DWORD ValueDW(LPCTSTR szName, DWORD dwDefault=0) { ???????? if(!m_hKey) return FALSE; ???????? DWORD dwValue = dwDefault; ???????? DWORD dwLen = sizeof(DWORD); ???????? RegQueryValueEx(m_hKey, szName, NULL, NULL, (LPBYTE)&dwValue, &dwLen); ???????? return dwValue; ???? } ? ???? BOOL SetSZ(LPCTSTR szName, LPCTSTR szValue, DWORD dwLen) { ???????? //Prefix ???????? if(!m_hKey) return FALSE; ???????? // ???????? return ERROR_SUCCESS==RegSetValueEx(m_hKey, szName, 0, REG_SZ, (LPBYTE)szValue, sizeof(TCHAR)*dwLen); ???? } ? ???? BOOL SetSZ(LPCTSTR szName, LPCTSTR szValue) { ???????? return SetSZ(szName, szValue, 1+lstrlen(szValue)); ???? } ? ???? BOOL SetDW(LPCTSTR szName, DWORD dwValue) { ???????? //Prefix ???????? if(!m_hKey) return FALSE; ???????? // ???????? return ERROR_SUCCESS==RegSetValueEx(m_hKey, szName, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD)); ???? } ? ???? BOOL SetBinary(LPCTSTR szName, LPBYTE lpbValue, DWORD dwLen) { ???????? //Prefix ???????? if(!m_hKey) return FALSE; ???????? // ???????? return ERROR_SUCCESS==RegSetValueEx(m_hKey, szName, 0, REG_BINARY, lpbValue, dwLen); ???? } ? ???? BOOL SetMultiSZ(LPCTSTR szName, LPCTSTR lpszValue, DWORD dwLen) { ???????? return ERROR_SUCCESS==RegSetValueEx(m_hKey, szName, 0, REG_MULTI_SZ, (LPBYTE)lpszValue, sizeof(TCHAR)*dwLen); ???? } ? ???? BOOL DeleteValue(LPCTSTR szName) { ???????? //Prefix ???????? if(!m_hKey) return FALSE; ???????? // ???????? return ERROR_SUCCESS==RegDeleteValue(m_hKey, szName); ???? } ? ???? BOOL DeleteKey(LPCTSTR szName) { ???????? if(!m_hKey) return FALSE; ???????? return ERROR_SUCCESS==RegDeleteKey(m_hKey, szName); ???? } }; |
?????? 具體的使用方法可參照后面代碼中LoadMemDrv()的實現(xiàn)。
2.Driver和DLL合并與解壓
?????? 合并方法很簡單。首先,我將DLL中的每個字節(jié)的數(shù)據(jù)提取出來組合成一個數(shù)組,然后在AP中引用該數(shù)組。
?????? 解壓的時候,直接將該數(shù)組組合成Driver的DLL就行了,如下:
| // 從靜態(tài)變量區(qū)提取MEM_DRV_NAME驅(qū)動的內(nèi)容,并將其組合成為一個Driver的dll { ???? HANDLE?? hTempFile = INVALID_HANDLE_VALUE; ???? DWORD??? dwBytesReturned = 0; ???? TCHAR??? szFileName[MAX_PATH/2] = {0,}; ? ???? wsprintf(szFileName, L"%s%s", TEXT("//"), MEM_DRV_NAME); ? ???? hTempFile = CreateFile( ???????? szFileName, ???????? GENERIC_READ|GENERIC_WRITE, ???????? FILE_SHARE_WRITE|FILE_SHARE_READ, ???????? NULL, ???????? CREATE_ALWAYS, ???????? FILE_ATTRIBUTE_NORMAL, ???????? NULL ???????? ); ? ???? if (INVALID_HANDLE_VALUE == hTempFile) ???? { ???????? LogMessage(TEXT("[ERR] Faild to create file. File name %s"), szFileName); ???? } ???? else ???? {???????????? ???????? // DllFile就是DLL變量數(shù)組的名字 ???????? // 這里將DLL的內(nèi)容寫入到前面創(chuàng)建的文件L"MyMemoryDrv.dll"中 ???????? if (!WriteFile(hTempFile, DllFile, sizeof(DllFile), &dwBytesReturned, NULL)) ???????? { ????????????? LogMessage(TEXT("[ERR] Faild to write file.? Error code 0x%x"), GetLastError()); ???????? } ???????? else ???????? {????????????????? ????????????? LogMessage(TEXT("Create driver %s successfully"), szFileName); ???????? } ? ???????? CloseHandle(hTempFile); ???????? ???????? DeleteFile(MEM_DRV_DST_PATH); ? ???????? //if (!CopyFile(szFileName, L"//me.dat", 0)) ???????? if (!CopyFile(szFileName, MEM_DRV_DST_PATH, FALSE)) ???????? { ????????????? LogMessage(L"[ERR] Copy memory driver from %s to %s failed, Error code 0x%x!", szFileName, MEM_DRV_DST_PATH, GetLastError()); ???????? } ???? }??????? } |
3.Driver的實現(xiàn)
?????? 由于該Driver的功能僅僅是在User Mode和Kernel Mode下的內(nèi)存之間做轉(zhuǎn)換,所以只需要簡單的實現(xiàn)一下DeviceIoControl就可以了,其它的流接口除了Open和Init直接為空就行了。
?????? 如下:
| /* * make use of MEM_IOControl to control memory address space conversion * Para: *??? pInBuf:?????? physical or virtual memory address *??? nInBufSize:?? 4 *??? pOutBuf:????? user buffer used to store data *??? nOutBufSize:? size of data the user wanted in bytes [note********************] */ DWORD MEM_IOControl(DWORD Handle, DWORD dwIoControlCode, PBYTE pInBuf, ?????????????????????? DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned) { ???? DWORD bRetVal = ERROR_SUCCESS; ???? PBYTE pMemBuffer = NULL; ???? ???? NKDbgPrintfW(L"MEM_IOControl(). Handle 0x%x, Code 0x%x, pInBuf 0x%8x, InSize 0x%x, OutSize 0x%x/r/n", Handle, dwIoControlCode, *(DWORD *)pInBuf, nInBufSize, nOutBufSize); ???? ??? switch(dwIoControlCode) { ???? case IOCTL_MEM_GET_PHYSICAL_RAM: ???????? NKDbgPrintfW(TEXT("IOCTL_MEM_GET_PHYSICAL_RAM/r/n")); ???????? break; ???? case IOCTL_MEM_GET_VIRTUAL_RAM: ???????? NKDbgPrintfW(TEXT("IOCTL_MEM_GET_VIRTUAL_RAM/r/n")); ???????? break; ???? default: ???????? NKDbgPrintfW(TEXT("**UNKNOWN**/r/n")); ???????? break; ??? } ???? ???? ??? switch(dwIoControlCode) { ???? case IOCTL_MEM_GET_PHYSICAL_RAM: ???????? {???????????? ????????????? do ????????????? {????????????????? ?????????????????? if (pInBuf == NULL || nInBufSize != sizeof(DWORD) || pOutBuf == NULL || nOutBufSize == 0) ?????????????????? { ?????????????????????? ?????????????????????? NKDbgPrintfW((_T("MEM_IOControl: IOCTL_MEM_GET_PHYSICAL_RAM - invalid paramter/n/r"))); ?????????????????????? bRetVal = ERROR_INVALID_PARAMETER; ?????????????????????? break; ?????????????????? } ?????????????????? ?????????????????? pMemBuffer = (PBYTE)VirtualAlloc(NULL, nOutBufSize, MEM_RESERVE, PAGE_NOACCESS); ?????????????????? ?????????????????? if (NULL != pMemBuffer) ?????????????????? { ?????????????????????? ?????????????????????? if (!VirtualCopy((void *)pMemBuffer, (void *)((*(DWORD *)pInBuf)>>8), nOutBufSize, PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL)) ?????????????????????? { ??????????????????????????? NKDbgPrintfW((_T("[MEMDRV] MEM_IOControl() : pMemBuffer VirtualCopy() Failed /n/r"))); ??????????????????????????? bRetVal = ERROR_INVALID_PARAMETER; ?????????????????????? } ?????????????????????? else ?????????????????????? { ??????????????????????????? ??????????????????????????? __try ??????????????????????????? {??? ???????????????????????????????? NKDbgPrintfW(L"Physical Add: 0x%8x, Virtual Add: 0x%8x/r/n", *(DWORD *)pInBuf, *(DWORD*)pMemBuffer); ???????????????????????????????? memcpy(pOutBuf, pMemBuffer, nOutBufSize); ??????????????????????????? } ??????????????????????????? __except(EXCEPTION_EXECUTE_HANDLER) ??????????????????????????? { ???????????????????????????????? NKDbgPrintfW((L"[ERR] Throw out exception in MEMDRV: MEM_IOControl()")); ???????????????????????????????? bRetVal = ERROR_INVALID_PARAMETER; ??????????????????????????? } ?????????????????????? } ?????????????????????? ?????????????????????? VirtualFree(pMemBuffer, 0, MEM_RELEASE); ?????????????????? }??? ?????????????????? else ?????????????????? { ?????????????????????? NKDbgPrintfW((_T("[MEMDRV] MEM_IOControl() : pMemBuffer VirtualCopy() Failed /n/r"))); ?????????????????????? bRetVal = ERROR_INVALID_PARAMETER;????????????????????? ?????????????????? } ????????????? } ????????????? while(0);????????? ????????????? ???????? } ???????? break; ???? case IOCTL_MEM_GET_VIRTUAL_RAM: ???????? {???????????? ????????????? NKDbgPrintfW(TEXT("IOCTL_MEM_GET_VIRTUAL_RAM/r/n")); ????????????? __try ????????????? {??? ?????????????????? // copy data from pInBuf to pOutBuf ?????????????????? memcpy(pOutBuf, &pInBuf, nOutBufSize);??? ????????????? } ????????????? __except(EXCEPTION_EXECUTE_HANDLER) ????????????? { ?????????????????? NKDbgPrintfW((L"[ERR] Throw out exception in MEMDRV: MEM_IOControl()")); ?????????????????? bRetVal = ERROR_INVALID_PARAMETER; ????????????? } ???????? } ???????? break; ???? default: ???????? NKDbgPrintfW(TEXT("**UNKNOWN**/r/n")); ???????? bRetVal = ERROR_INVALID_PARAMETER; ???????? break; ??? } ???? ???? return (ERROR_SUCCESS == bRetVal); } |
4.Driver的加載????
?????? Driver的加載包括兩個過程。首先將Driver相關(guān)的注冊表項寫入到注冊表中,然后調(diào)用API ActivateDevice()來實現(xiàn)動態(tài)的加載。
?????? 代碼如下:
| /* ? [HKEY_LOCAL_MACHINE/Drivers/BuiltIn/MEM] "Dll"="MyMemoryDrv.dll" "Prefix"="MEM" "Index"=dword:1 "Order"=dword:0 "FriendlyName"="MEM driver" ? */ #define MEM_DRV_NAME???????????? L"MEM1:" bool? LoadMemDrv(void) { ???? BOOL bRetVal = false; ? ???? // Step1: modify the registry ???? class CReg MemDrvReg; ? ???? bRetVal = MemDrvReg.Create(HKEY_LOCAL_MACHINE, MEM_DRV_PATH); ???? bRetVal= MemDrvReg.SetSZ(L"Dll", L"MyMemoryDrv.dll");//, sizeof(L"MyMemoryDrv.dll")/sizeof(TCHAR)); ???? MemDrvReg.SetSZ(L"Prefix", L"MEM");//, sizeof(L"MEM")/sizeof(TCHAR)); ???? MemDrvReg.SetDW(L"Order", 0); ???? MemDrvReg.SetDW(L"Index", 1); ???? MemDrvReg.SetDW(L"Index", 1); ???? MemDrvReg.SetSZ(L"FriendlyName", L"MEM driver");//, sizeof(L"MEM driver")/sizeof(TCHAR)); ? ???? // Step2: load driver unsing device manager ???? hActiveMemDrv = INVALID_HANDLE_VALUE; ? ???? hActiveMemDrv = ActivateDevice(MEM_DRV_PATH, 0); ???? if (INVALID_HANDLE_VALUE == hActiveMemDrv) ???? { ???????? LogMessage(L"[ERR]Load driver %s failed", MEM_DRV_FULL_PATH); ???????? goto EXIT; ???? } ? ???? // Step3: Open stream driver???? ???? hFile = INVALID_HANDLE_VALUE; ???? hFile = CreateFile(MEM_DRV_NAME, ???????? GENERIC_READ|GENERIC_WRITE, ???????? 0, ???????? NULL, ???????? OPEN_EXISTING, ???????? FILE_ATTRIBUTE_NORMAL, ???????? NULL); ???? ???? if (INVALID_HANDLE_VALUE == hFile) ???? { ???????? LogMessage(L"[ERR] Open stream driver %s failed. Error code 0x%8x", MEM_DRV_NAME, GetLastError()); ???????? goto EXIT; ???? } ? ???? bIsDrvLoad = true; ???? bRetVal = TRUE; ? EXIT:??? ???? return (bRetVal == TRUE); } |
附:
具體的實現(xiàn)代碼可以到我的資源中下載。
總結(jié)
以上是生活随笔為你收集整理的一种User Mode下访问物理内存及Kernel Space的简单实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蔚来单日换电量首次突破 5 万次,春节期
- 下一篇: 一加11R现身印度官网:低频版骁龙8+、