標 題: 【原創】我寫的模擬掛 作 者: asrn 時 間: 2013-05-07,23:32:09 鏈 接: http://bbs.pediy.com/showthread.php?t=171203 發出來,一為拋磚引玉,論壇應該也有很多xd想寫外掛,可以參考下;二來想出去找份工作,本人年近30歲,而且還沒有編碼的工作經驗,沒有信心,希望大家能給點意見。。 驅動最初參考了http://bbs.pediy.com/showthread.php?t=101653 中的代碼,因為是根據特征碼搜索,不爽,后面又根據寒江獨釣中的代碼作了修改(這里要澄清下,不是為寒江獨釣打廣告,反而我覺得那書寫得不清不楚的)。 原理:通過直接調用Kbdclass的回調函數KeyboardClassServiceCallback直接給上層發送鍵盤驅動,就可以實現模擬鍵盤操作,鼠標類似。 通過windbg查看類設備下面的端口設備(i8042prt)或usb設備(kbdhid),其設備對象中的DeviceExtension里面保存了設備對象與KeyboardClassServiceCallback回調函數,設備對象保存在回調函數前面一個地址中。 這個是驅動擴展結構,用來保存查找到的設備對象和回調函數,避免直接使用全局變量 typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT kbdDeviceObject; //鍵盤類設備對象 PDEVICE_OBJECT mouDeviceObject; //鼠標類設備對象 MY_KEYBOARDCALLBACK My_KbdCallback; //KeyboardClassServiceCallback函數 MY_MOUSECALLBACK My_MouCallback; //MouseClassServiceCallback函數 }DEVICE_EXTENSION, *PDEVICE_EXTENSION; 下面是查找KeyboardClassServiceCallback的關鍵函數,鼠標設備查找方法類似,我合成了一個函數 NTSTATUS GetKmclassInfo(PDEVICE_OBJECT DeviceObject, USHORT Index) { NTSTATUS status; UNICODE_STRING ObjectName; PCWSTR kmhidName, kmclassName, kmName; PVOID kmDriverStart; ULONG kmDriverSize; PVOID* TargetDeviceObject; PVOID* TargetclassCallback; PDEVICE_EXTENSION deviceExtension; PDRIVER_OBJECT kmDriverObject = NULL; PDRIVER_OBJECT kmclassDriverObject = NULL; deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; switch(Index) { case KEYBOARD_DEVICE: kmName = L"kbd"; kmhidName = L"\\Driver\\kbdhid"; kmclassName = L"\\Driver\\kbdclass"; TargetDeviceObject = (PVOID*)&(deviceExtension->kbdDeviceObject); TargetclassCallback = (PVOID*)&(deviceExtension->My_KbdCallback); break; case MOUSE_DEVICE: kmName = L"mou"; kmhidName = L"\\Driver\\mouhid"; kmclassName = L"\\Driver\\mouclass"; TargetDeviceObject = (PVOID*)&(deviceExtension->mouDeviceObject); TargetclassCallback = (PVOID*)&(deviceExtension->My_MouCallback); break; default: return STATUS_INVALID_PARAMETER; } // 通過USB類設備獲取驅動對象 RtlInitUnicodeString(&ObjectName, kmhidName); status = ObReferenceObjectByName(&ObjectName, OBJ_CASE_INSENSITIVE, NULL, FILE_READ_ACCESS, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&kmDriverObject); if(!NT_SUCCESS(status)) { // 通過i8042prt獲取驅動對象 RtlInitUnicodeString(&ObjectName, L"\\Driver\\i8042prt"); status = ObReferenceObjectByName(&ObjectName, OBJ_CASE_INSENSITIVE, NULL, FILE_READ_ACCESS, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&kmDriverObject); if(!NT_SUCCESS(status)) { KdPrint(("Couldn't Get the i8042prt Driver Object\n")); return status; } } // 通過kmclass獲取鍵盤鼠標類驅動對象 RtlInitUnicodeString(&ObjectName, kmclassName); status = ObReferenceObjectByName(&ObjectName, OBJ_CASE_INSENSITIVE, NULL, FILE_READ_ACCESS, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&kmclassDriverObject); if(!NT_SUCCESS(status)) { KdPrint(("Couldn't Get the kmclass Driver Object\n")); return status; } else { kmDriverStart = kmclassDriverObject->DriverStart; kmDriverSize = kmclassDriverObject->DriverSize; } ULONG DeviceExtensionSize; PULONG kmDeviceExtension; PDEVICE_OBJECT kmTempDeviceObject; PDEVICE_OBJECT kmclassDeviceObject; PDEVICE_OBJECT kmDeviceObject = kmDriverObject->DeviceObject; while (kmDeviceObject) { kmTempDeviceObject = kmDeviceObject; while (kmTempDeviceObject) { kmDeviceExtension = (PULONG)kmTempDeviceObject->DeviceExtension; kmclassDeviceObject = kmclassDriverObject->DeviceObject; DeviceExtensionSize = ((ULONG)kmTempDeviceObject->DeviceObjectExtension - (ULONG)kmTempDeviceObject->DeviceExtension) / 4; while (kmclassDeviceObject) { for (ULONG i = 0; i < DeviceExtensionSize; i++) { if (kmDeviceExtension[i] == (ULONG)kmclassDeviceObject && kmDeviceExtension[i + 1] > (ULONG)kmDriverStart && kmDeviceExtension[i + 1] < (ULONG)kmDriverStart + kmDriverSize) { // 將獲取到的設備對象保存到自定義擴展設備結構 *TargetDeviceObject = (PVOID)kmDeviceExtension[i]; *TargetclassCallback = (PVOID)kmDeviceExtension[i + 1]; KdPrint(("%SDeviceObject == 0x%x\n", kmName, kmDeviceExtension[i])); KdPrint(("%SClassServiceCallback == 0x%x\n", kmName, kmDeviceExtension[i + 1])); return STATUS_SUCCESS; } } kmclassDeviceObject = kmclassDeviceObject->NextDevice; } kmTempDeviceObject = kmTempDeviceObject->AttachedDevice; } kmDeviceObject = kmDeviceObject->NextDevice; } return STATUS_UNSUCCESSFUL; } 應用層模擬鍵盤操作函數 BOOL KeyboardButton(USHORT VirtualKey, USHORT Flags) { KEYBOARD_INPUT_DATA kid ; DWORD dwOutput; HANDLE hDevice = CreateFile(KEYMOUSE_WIN32_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) return FALSE; memset(&kid, 0, sizeof(KEYBOARD_INPUT_DATA)); kid.Flags = Flags; kid.MakeCode = (USHORT)MapVirtualKey(VirtualKey, 0); BOOL bRet = DeviceIoControl(hDevice, IOCTL_KEYBOARD, &kid, sizeof(KEYBOARD_INPUT_DATA), NULL, 0, &dwOutput, NULL); if (!bRet) TRACE(_T("Error! please open the simulate kmclass driver!\n")); CloseHandle(hDevice); return bRet; } 模擬鼠標的函數 BOOL MouseMove(LONG dx, LONG dy, USHORT Flags) { MOUSE_INPUT_DATA mid ; DWORD dwOutput; HANDLE hDevice = CreateFile(KEYMOUSE_WIN32_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) return FALSE; memset(&mid, 0, sizeof(MOUSE_INPUT_DATA)); mid.Flags = Flags; switch (mid.Flags) { case MOUSE_MOVE_RELATIVE: mid.LastX = dx; mid.LastY = dy; break; case MOUSE_MOVE_ABSOLUTE: mid.LastX = dx * 0xffff / GetSystemMetrics(SM_CXSCREEN); mid.LastY = dy * 0xffff / GetSystemMetrics(SM_CYSCREEN); break; default: TRACE(_T("Flags: Parameter error!\n")); return FALSE; } BOOL bRet = DeviceIoControl(hDevice, IOCTL_MOUSE, &mid, sizeof(MOUSE_INPUT_DATA), NULL, 0, &dwOutput, NULL); if (!bRet) TRACE(_T("Error! please start the kmclass driver!\n")); CloseHandle(hDevice); return bRet; } 另外一個是前台窗口找圖的實現 bmp類定義 class Cbm { private: BITMAPFILEHEADER bmfh; // 位圖文件頭 BITMAPINFOHEADER bmih; // 位圖信息頭 PBYTE pBits; // 位圖像素位指針 int cBits; // 位圖每行所用字節總數 int cxDib; // 位圖水平像素寬度 int cyDib; // 位圖垂直像素高度 void SetcBits() {cBits = ((cxDib * bmih.biBitCount + 31) & ~31) >> 3;} void SetcxDib() {cxDib = bmih.biWidth;} void SetcyDib() {cyDib = bmih.biHeight;} .... } // 通過窗口圖像獲取位圖信息 Cbm::Cbm(HWND hwndScreen) { HDC hdc, hdcMem, hdcScreen; HBITMAP hBitmap; RECT rect; if (!hwndScreen) { memset(&rect, 0, sizeof(RECT)); rect.right = GetSystemMetrics(SM_CXSCREEN); rect.bottom = GetSystemMetrics(SM_CYSCREEN); }else GetClientRect(hwndScreen, &rect); //獲得截圖窗口的范圍大小 hdc = GetDC(NULL); hdcMem = CreateCompatibleDC(hdc); hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top); SelectObject(hdcMem, hBitmap); hdcScreen = GetDC(hwndScreen); BitBlt(hdcMem, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hdcScreen, 0, 0, SRCCOPY); DeleteDC(hdcMem); ReleaseDC(hwndScreen, hdcScreen); //初始化信息頭bmi結構 memset(&bmih, 0, sizeof(BITMAPINFOHEADER)); bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = rect.right - rect.left; bmih.biHeight = rect.bottom - rect.top; bmih.biBitCount = 24; bmih.biCompression = BI_RGB; bmih.biPlanes = 1; SetcxDib(); SetcyDib(); SetcBits(); //獲取pBits的值 pBits = new BYTE [cBits * cyDib]; GetDIBits(hdc, hBitmap, 0, cyDib, pBits, (LPBITMAPINFO)&bmih, DIB_RGB_COLORS); //初始化文件頭bmfh bmfh.bfType = 0x4D42; bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + cBits * cyDib; bmfh.bfReserved1 = 0; bmfh.bfReserved2 = 0; bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); ReleaseDC(NULL, hdc); DeleteObject(hBitmap); } // 通過加載文件獲取位圖信息 Cbm::Cbm(PCTSTR FilePath) { HANDLE hFile = CreateFile(FilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile == INVALID_HANDLE_VALUE) { pBits = NULL; TRACE(_T("read file failed. FileName: %s\n"), FilePath); return; } DWORD dwBytesRead; if ( !(ReadFile(hFile, &bmfh, sizeof(BITMAPFILEHEADER), &dwBytesRead, NULL) && ReadFile(hFile, &bmih, sizeof(BITMAPINFOHEADER), &dwBytesRead, NULL) && bmfh.bfType == 0x4D42) ) { pBits = NULL; TRACE(_T("read file failed. FileName: %s\n"), FilePath); CloseHandle(hFile); return; } SetcxDib(); SetcyDib(); SetcBits(); pBits = new BYTE [cBits * cyDib]; if (!ReadFile(hFile, pBits, cBits * cyDib, &dwBytesRead, NULL)) { delete [] pBits; pBits = NULL; TRACE(_T("read file failed. FileName: %s\n"), FilePath); } CloseHandle(hFile); } // 保存位圖到文件 BOOL Cbm::SaveBitmapToFile(PCTSTR FileName, LPCRECT pRect) const { ASSERT(pBits); TCHAR FilePath[MAX_PATH], DefaultFileName[MAX_PATH]; //創建以系統時間命名的bmp文件 SYSTEMTIME time; GetLocalTime(&time); wsprintf(DefaultFileName, _T("%04u%02u%02u%02u%02u%02u%03u.bmp"), time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds); //修正保存路徑,默認保存至當前程序目錄Screen文件夾 if (!FileName) wsprintf(FilePath, _T("%s\\%s"), _T("screen"), DefaultFileName); else { if (FileName[1] == ':') _tcscpy_s(FilePath, FileName); else wsprintf(FilePath, _T("%s\\%s"), _T("screen"), FileName); if (FileName[lstrlen(FileName) - 1] == '\\' || FileName[lstrlen(FileName) - 1] == '/') _tcscat_s(FilePath, MAX_PATH, DefaultFileName); } // 判斷文件路徑是否有效,無效則創建路徑中沒有的文件夾 if (!PathIsDirectory(FilePath)) CreateFolder(FilePath); //保存數據 HANDLE hFile = CreateFile(FilePath, GENERIC_WRITE, 0 ,NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return FALSE; DWORD dwBytesWritten; Cbm bmFile(*this, pRect); BOOL bSuccess = WriteFile(hFile, &bmFile.bmfh, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL) && WriteFile(hFile, &bmFile.bmih, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL) && WriteFile(hFile, bmFile.pBits, bmFile.cBits * bmFile.cyDib, &dwBytesWritten, NULL); CloseHandle(hFile); if (!bSuccess) DeleteFile(FilePath); return bSuccess; } 找圖函數 BOOL FindPic(const Cbm & bmWnd, const Cbm & bmFile, LPCRECT rectTarget, OUT PRECT retRect, int resemble, COLORREF rgb) { if (!(bmFile.pBits && bmWnd.pBits) || bmFile.cxDib > bmWnd.cxDib || bmFile.cyDib > bmWnd.cyDib) return FALSE; resemble = max(resemble, 0); resemble = min(resemble, 100); BYTE r = GetRValue(rgb); BYTE g = GetGValue(rgb); BYTE b = GetBValue(rgb); // 實際范圍 RECT rectDefault; if (rectTarget && bmWnd.IsInRect(*rectTarget)) rectDefault = *rectTarget; else bmWnd.GetBitmapRect(rectDefault); // bmFile圖像坐標(x, y), bmWnd圖像坐標(x + xOffset, y + yOffset) int yTotal = rectDefault.bottom - bmFile.cyDib; int xTotal = rectDefault.right - bmFile.cxDib; int invalidTotal = (100 - resemble) * (bmFile.cxDib * bmFile.cyDib); int validTotal = resemble * (bmFile.cxDib * bmFile.cyDib); // ignoreNum忽略值, validNum有效值,invalidNum無效值 int invalidNum = 0, validNum = 0, ignoreNum = 0; for (int yOffset = rectDefault.top; yOffset <= yTotal; yOffset++) for (int xOffset = rectDefault.left; xOffset <= xTotal; xOffset++) { for (int y = 0, bflag = TRUE; bflag && (y < bmFile.cyDib); y++) for (int x = 0; x < bmFile.cxDib; x++) { int FileIndex = (bmFile.cyDib - 1 - y) * bmFile.cBits + 3 * x; int WndIndex = (bmWnd.cyDib - 1 - yOffset - y) * bmWnd.cBits + 3 * (xOffset + x); if (r == bmFile.pBits[FileIndex + 2] && g == bmFile.pBits[FileIndex + 1] && b == bmFile.pBits[FileIndex] && 0xF8 != bmWnd.pBits[WndIndex + 2] && 0xFC != bmWnd.pBits[WndIndex + 1] && 0xF8 != bmWnd.pBits[WndIndex]) { ignoreNum++; } else if (bmFile.pBits[FileIndex + 2] == bmWnd.pBits[WndIndex + 2] && bmFile.pBits[FileIndex + 1] == bmWnd.pBits[WndIndex + 1] && bmFile.pBits[FileIndex] == bmWnd.pBits[WndIndex]) { validNum++; } else invalidNum++; if (100 * invalidNum > invalidTotal) { invalidNum = validNum = ignoreNum = 0; bflag = FALSE; break; } if (100 * (validNum + ignoreNum) >= validTotal) { if (retRect) { retRect->left = xOffset; retRect->top = yOffset; retRect->right = xOffset + bmFile.cxDib; retRect->bottom = yOffset + bmFile.cyDib; } return TRUE; } } } return FALSE; } 多圖查找函數 BOOL FindSomePic(const Cbm & bmWnd, PCTSTR FileName, LPCRECT rectTarget, PRECT retRect, PTSTR retFileName, int resemble, COLORREF rgb) { WIN32_FIND_DATA fData; BOOL bFind = FALSE; TCHAR FilePath[MAX_PATH]; TCHAR FileDir[MAX_PATH]; _tcscpy_s(FilePath, MAX_PATH, FileName); _tcscpy_s(FileDir, MAX_PATH, FileName); if (FileName[lstrlen(FileName) - 1] == '\\') _tcscat_s(FilePath, MAX_PATH, _T("*.bmp")); else if (_tcschr(FileName, '*')) _tcsrchr(FileDir, '\\')[1] = '\0'; else { bFind = FindPic(bmWnd, FileName, rectTarget, retRect, resemble, rgb); if (retFileName) { if (bFind) _tcscpy_s(retFileName, MAX_PATH, FileName); else retFileName[0] = '\0'; } return bFind; } HANDLE hFile = FindFirstFile(FilePath, &fData); if (hFile == INVALID_HANDLE_VALUE) { TRACE(_T("FindSomePic --- read file failed.\n")); return FALSE; } do{ wsprintf(FilePath, _T("%s%s"), FileDir, fData.cFileName); bFind = FindPic(bmWnd, FilePath, rectTarget, retRect, resemble, rgb); }while (!bFind && FindNextFile(hFile, &fData)); FindClose(hFile); if (retFileName) { if (bFind) _tcscpy_s(retFileName, MAX_PATH, fData.cFileName); else retFileName[0] = '\0'; } return bFind; } 忘記說了,模擬鼠標移動需要關閉 控制面板->鼠標->指針選項->提高指針精確度 這個選項 整個項目是VS2008創建,驅動工程是通過visualddk的向導添加的。 驅動在XP、win7下測試通過 附件有完整項目的代碼