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 ...
随机推荐
- zXing使用注意事项-转
zXing使用注意事项 ------------------ zxing和zbar的比较: ZXing项目的示例程序对于摄像头的控制写的非常全面,ZBar的没有ZBar基于C语言编写,解码效率高于 ...
- e788. 取消JSpinner的键盘编辑能力
// Create a nummber spinner JSpinner spinner = new JSpinner(); // Disable keyboard edits in the spin ...
- e859. 将键盘事件和字符串对应
The KeyStroke.toString() method does not return a string that can be parsed by KeyStroke.getKeyStrok ...
- 正则表达式-----------------------------------C#的正则表达式
为了避免以后这样的情况,在此记录下正则表达式的一些基本使用方法附带小的实例.让以后在使用时能一目了然知道他的使用,为开发节约时间,同时也分享给大家 正则元字符 在说正则表达式之前我们先来看看通配符,我 ...
- Kafka 之 入门
摘要: 最近研究采集层,对Kafka做了一个研究.分为入门,中级,高级步步进阶.本篇主要介绍基本概念,适用场景. 一.入门 1. 简介 Kafka is a distributed, parti ...
- Unity3d 动态加载材质方法
Texture img = (Texture)Resources.Load("LedPicture"); GameObject.Find("Led").rend ...
- Nginx访问日志 Nginx日志切割 静态文件不记录日志和过期时间
- sp_replication_agent_checkup
Transact-SQL 参考sp_replication_agent_checkup 检查每个分发数据库的复制代理程序,这些复制代理程序正在运行但在指定的心跳信号间隔内没有历史记录.此存储过程在分发 ...
- Markdown编辑器Editor.md使用方式
摘要: 搭建个人博客时,涉及文章上传,文章展示,这里需要一个Markdown插件,mark一下. Editormd下载地址:http://pandao.github.io/editor.md/ 由于前 ...
- 【WP8】富文本功能实现
2014年8月1日更新:修复如果有多个相同链接解析失败的Bug,谢谢 @Walsh 提供的问题 富文本在移动APP上应用的最多的就是表情了,类似微博,QQ,微信都有对提供对表情和链接的支持,富文本一般 ...