发出来,一为抛砖引玉,论坛应该也有很多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下测试通过
附件有完整项目的代码
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)