xp下对dinput8.dll 游戏键盘输入的模拟 非函数hook
https://www.xuebuyuan.com/833929.html
很多游戏或者3d模拟软件为了更好的支持外设使用directinput作为输入接口调用。那么如果要模拟鼠标或键盘来控制游戏或者3d软件进行自动作业如何才能做到呢?
我研究了键盘部分。鼠标应该以此类推
入手模块:dinput8.dll
使用软件:idapro5.0,ollydbg,C32asm
思路是这样的。在进行dinput编程的时候有一个循环查询状态的处理。被调用的函数为CKbd_GetDeviceState (此函数地址可以从idapro5.0的分析结果中找到)
调用代码类似:
HRESULT UpdateInputState(void)
{
DWORD i;
if(lpKeyboard != NULL)
{
DIDEVICEOBJECTDATA didod[DINPUT_BUFFERSIZE]; // Receives buffered data
DWORD dwElements;
HRESULT hr;
hr = DIERR_INPUTLOST;
while(hr != DI_OK)
{
dwElements = DINPUT_BUFFERSIZE;
hr = lpKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),didod,&dwElements,0); 这里调用 CKbd_GetDeviceState
if (hr != DI_OK)
{
hr = lpKeyboard->Acquire();
if(FAILED(hr))
return hr;
}
}
if(FAILED(hr))
return hr;
}
for(int i=0; i<dwElements; i++)
{
// 此处放入处理代码
// didod[i].dwOfs 表示那个键被按下或松开
// didod[i].dwData 记录此键的状态,低字节最高位是 1 表示按下,0 表示松开
// 一般用 didod[i].dwData&0x80 来测试
}
return S_OK;
}
那么键盘状态是如何被获得的呢?请看下面的ida分析结果
.text:6D18C5EA _CKbd_GetDeviceState@8 proc near ; DATA XREF: .text:6D18C37Co
.text:6D18C5EA
.text:6D18C5EA arg_0 = dword ptr 8
.text:6D18C5EA arg_4 = dword ptr 0Ch
.text:6D18C5EA
.text:6D18C5EA mov edi, edi
.text:6D18C5EC push ebp
.text:6D18C5ED mov ebp, esp
.text:6D18C5EF mov eax, [ebp+arg_0]
.text:6D18C5F2 mov ecx, [eax+8]
.text:6D18C5F5 test byte ptr [ecx], 2
.text:6D18C5F8 jz short loc_6D18C60D
.text:6D18C5FA push esi
.text:6D18C5FB mov esi, [eax+4] ;根据跟踪分析。esi指向的内存为一个键盘状态表不同的键位如果按下为0x80,没有按下为00
.text:6D18C5FE push edi
.text:6D18C5FF mov edi, [ebp+arg_4]
.text:6D18C602 push 40h ;拷贝长度为0x100字节40h*4
.text:6D18C604 pop ecx
.text:6D18C605 rep movsd ;拷贝键盘状态给外部接收缓冲区
.text:6D18C607 pop edi
.text:6D18C608 xor eax, eax
.text:6D18C60A pop esi
.text:6D18C60B jmp short loc_6D18C612
.text:6D18C60D ; ---------------------------------------------------------------------------
.text:6D18C60D
.text:6D18C60D loc_6D18C60D: ; CODE XREF: CKbd_GetDeviceState(x,x)+Ej
.text:6D18C60D mov eax, 8007001Eh
.text:6D18C612
.text:6D18C612 loc_6D18C612: ; CODE XREF: CKbd_GetDeviceState(x,x)+21j
.text:6D18C612 pop ebp
.text:6D18C613 retn 8
.text:6D18C613 _CKbd_GetDeviceState@8 endp
通过跟踪得知存放缓冲区的是一个全局变量内存区 键盘表对应键位如下 我列出常用键。
基地址为:6d1a4448 这个地址可以通过跟踪esi的内容得到
6d1a4448h+2 = 1 到 6d1a4448h+bh = 0
6d1a4448h+ch = -
6d1a4448h+dh = =
6d1a4448h+1eh = a
6d1a4448h+30h = b
6d1a4448h+2eh = c
6d1a4448h+20h = d
6d1a4448h+12h = e
6d1a4448h+21h = f
6d1a4448h+22h = g
6d1a4448h+23h = h
6d1a4448h+17h = i
6d1a4448h+24h = j
6d1a4448h+25h = k
6d1a4448h+26h = l
6d1a4448h+32h = m
6d1a4448h+31h = n
6d1a4448h+18h = o
6d1a4448h+19h = p
6d1a4448h+10h = q
6d1a4448h+13h = r
6d1a4448h+1fh = s
6d1a4448h+14h = t
6d1a4448h+16h = u
6d1a4448h+2fh = v
6d1a4448h+11h = w
6d1a4448h+2dh = x
6d1a4448h+15h = y
6d1a4448h+2ch = z
6d1a4448h+1ch = enter
6d1a4448h+c8h = up
6d1a4448h+d0h = down
6d1a4448h+cbh = left
6d1a4448h+cdh = right
那么如何在ollydbg中跟踪调试呢.
我们可以通过c32asm 修改CKbd_GetDeviceState 6D1880A7 8BFF mov edi, edi ;
函数的前一个字节的机器码为cc也就是int 3断点
目前是8BFF修改为CCFF 那么当执行到这个函数的时候会提示发现调试位置错误0x80000003根据提示进入调试
首先要把ollydbg设置为系统默认调试器。
在ollydbg中会停在
CC int3 ; CKbd_GetDeviceState
FF55 8B call dword ptr [ebp-75]
EC in al, dx
8B45 08 mov eax, dword ptr [ebp+8]
ctrl+e 修改cc为8b
单步走f8到这句。
.text:6D18C5FB mov esi, [eax+4]
再按一下f8
esi中就有地址了
察看esi指向的内容。会看到一片都是0。这里就是键盘缓冲区
那么既然知道键盘缓冲区地址固定。我们就可以编码模拟了。
首先要做的就是插入我们的代码到要修改的进程里面去。用hook也好远程线程也好。方法很多不讲了。
我这里使用的是键盘hook
BYTE keyMap[0x100]={NULL};
BYTE *dinput8KeyMap=(BYTE*)0x24448; //键盘映射区地址偏移
//在程序的初始化处计算缓冲区相对位置
HINSTANCE hDinput8 = 0;
hDinput8 = GetModuleHandle("dinput8.dll");
if(!hDinput8)
{
__asm
{
mov eax,dinput8KeyMap
add eax,hDinput8
mov dinput8KeyMap,eax
}
}else
{
hDinput8 = LoadLibrary("dinput8.dll");
__asm
{
mov eax,dinput8KeyMap
add eax,hDinput8
mov dinput8KeyMap,eax
}
FreeLibrary(hDinput8);
}
//先定义我们的按键表编写一个初始化函数
void InitKeyMap()
{
ZeroMemory(keyMap,0x100);
keyMap[0x2] ='1';
keyMap[0x3] ='2';
keyMap[0x4] ='3';
keyMap[0x5] ='4';
keyMap[0x6] ='5';
keyMap[0x7] ='6';
keyMap[0x8] ='7';
keyMap[0x9] ='8';
keyMap[0xa] ='9';
keyMap[0xb] ='0';
keyMap[0xc] ='-';
keyMap[0xd] ='=';
keyMap[0x1e] ='a';
keyMap[0x30] ='b';
keyMap[0x2e] ='c';
keyMap[0x20] ='d';
keyMap[0x12] ='e';
keyMap[0x21] ='f';
keyMap[0x22] ='g';
keyMap[0x23] ='h';
keyMap[0x17] ='i';
keyMap[0x24] ='j';
keyMap[0x25] ='k';
keyMap[0x26] ='l';
keyMap[0x32] ='m';
keyMap[0x31] ='n';
keyMap[0x18] ='o';
keyMap[0x19] ='p';
keyMap[0x10] ='q';
keyMap[0x13] ='r';
keyMap[0x1f] ='s';
keyMap[0x14] ='t';
keyMap[0x16] ='u';
keyMap[0x2f] ='v';
keyMap[0x11] ='w';
keyMap[0x2d] ='x';
keyMap[0x15] ='y';
keyMap[0x2c] ='z';
keyMap[0x1c] =VK_RETURN;
keyMap[0xc8] =VK_UP;
keyMap[0xd0] =VK_DOWN;
keyMap[0xcb] =VK_LEFT;
keyMap[0xcd] =VK_RIGHT;
}
//编写一个键盘按下设置为80的函数
void SetKeyDown(BYTE vk)
{
//大小写转换
if(vk>='A' && vk<='Z')
{
vk|=0x20;
}
for(int cnt=0;cnt<0x100;cnt++)
{
if(keyMap[cnt])
{
if(keyMap[cnt]==vk)
{
dinput8KeyMap[cnt]=0x80;
break;
}
}
}
}
//这个函数偷懒。如果按键弹起我们就全部清理0
void SetKeyUp(BYTE vk)
{
ZeroMemory(dinput8KeyMap,0x100);
}
//我用的键盘钩子。这样实现的
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if(lParam==0xc0000001)
{
SetKeyDown((BYTE)wParam);
}
if(lParam==1)
{
SetKeyUp((BYTE)wParam);
}
return CallNextHookEx(winKbd,nCode,wParam,lParam);
}
这样就完成了.可以通过程序自动控制了.是不是很有意思
开发环境:
vs2005,xp sp2
jpg改rar
xp下对dinput8.dll 游戏键盘输入的模拟 非函数hook的更多相关文章
- 关于WinIO.DLL的键盘输入模拟
关于WinIO.DLL的键盘输入模拟 最近在找键盘模拟的方式,最后在网上找到了一个WinIO.DLL的IO键盘模拟按键的方式.但是居然那个方法是有问题的.我造了全局的hook监视键盘信息,发现它只是有 ...
- win2k,XP下用setupapi.dll自动安装Driver
win2k,XP下用setupapi.dll自动安装Driver 在驱网看到54cndr 写的这篇文章,虽然自己一直都用Installshield,但还是觉得这个也是一个很好的思路,故摘录在此. 用s ...
- 在Delphi中使用键盘勾子获取键盘输入(译--5月7日)
http://blog.sina.com.cn/s/blog_502b2e970100949s.html 获取键盘输入以控制无法接受输入焦点的控件考虑一些游戏,显示图片在TPainBox,但是TPai ...
- pyautogui页面点击和键盘输入
以下程序实现了在编辑框处点击,然后用键盘输入的功能 import pyautogui import time time.sleep(10) currentMouseX, currentMouseY = ...
- C# Winform中无焦点状态下获取键盘输入或者USB扫描枪数据
类文件: C#类文件 using System; using System.Collections.Generic; using System.Text; using System.Runtime.I ...
- 【Visual C++】游戏编程学习笔记之七:键盘输入消息
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...
- 代码实现:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名
package com.loaderman.test; import java.io.File; import java.io.FileReader; import java.util.Scanner ...
- VB模拟键盘输入的N种方法
VB模拟键盘输入的N种方法http://bbs.csdn.net/topics/90509805hd378发表于: 2006-12-24 14:35:39用VB模拟键盘事件的N种方法 键盘是我们使用计 ...
- 使用C#模拟键盘输入、鼠标移动和点击、设置光标位置及控制应用程序的显示
1.模拟键盘输入(SendKeys) 功能:将一个或多个按键消息发送到活动窗口,就如同在键盘上进行输入一样. 语法:SendKeys.Send(string keys);SendKeys.SendWa ...
随机推荐
- Python——pyiso8601
该模块不是Python内建的模块,为Python补充了 ISO 8601 解析——将常见的 ISO 8601 日期字符创转化为 Python 的 datetime 对象. 安装 $ pip insta ...
- Css 去除浮动
清除浮动的方法 清除浮动方法大致有两类,一类是clear:both | left | right ,另一类则是创建BFC,细分又可以分为多种. 通过在浮动元素末尾添加一个空的标签例如并设置样式为cle ...
- hive cst 时间转换
select from_unixtime(unix_timestamp(r.collecttime,'EEE MMM dd HH:mm:ss zzz yyyy'),'yyyy-MM-dd HH:mm: ...
- EF6 Code First & Auto Migration on Appharbor
之前不小心看到EF的code first在appharbor上进行migration的时候比较麻烦,今天碰巧也要更新数据库了,顺便试试. modify model public class SiteI ...
- Selenium Webdriver 的 PageObject 改造
PageObject中提供了一个@FindBy注解,也非常好用,但由于其是一次性全部初始化所有的WebElement,对于当前还不存在于页面上的Element在初始化时就会报错,为了解决这个问题,自然 ...
- react解析html标签组成的字符串
转载自:https://blog.csdn.net/tongshuo_11/article/details/61195232 var content = '<strong>content& ...
- Timeout in android httpclient
原文: http://www.cnblogs.com/codingmyworld/archive/2011/08/17/2141706.html /* 从连接池中取连接的超时时间 */ConnMana ...
- sql server 2014 序列號
亲测可用 27HMJ-GH7P9-X2TTB-WPHQC-RG79R
- MVC输出字符串常用四个方式
"); ";//@Html.Raw(s1); "); ";//@String.fomart(s3); 要MVC的Razor视图输出字符串的常用几个方式 记录下 ...
- v9定时发布的简单实现方法[支持静态生成]
将以下代码放到 api/count.php 文件最后 的 ?>之前 //add 定时发布审核功能 $modelid = $modelid ? $modelid : intval($_GET['m ...