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 ...
随机推荐
- (笔记)boa服务器make错误
编译一个linux下的c系统,包含词法和语法分析模块,Linux上用bison和flex.yacc是一个文法分析器的生成器,bison即是yacc的GNU版本.Lex和YACC是用于构造词法分析机和语 ...
- IntelliJ IDEA 2017 汉化包及教程
一.准备 官网下载IntelliJ IDEA 2017 并安装好 下载汉化包 (百度云链接:http://pan.baidu.com/s/1slS9ZMP 密码:gp79) 二.汉化 此处有两种方法, ...
- CI框架 -- 核心文件 之 Input.php(输入数据处理文件)
class CI_Input { //用户ip地址 protected $ip_address = FALSE; //用户浏览器地址 protected $user_agent = FALSE; // ...
- CI框架 -- URL
移除 URL 中的 index.php 默认情况,你的 URL 中会包含 index.php 文件: example.com/index.php/news/article/my_article 如果你 ...
- 使用Camera功能 AREA的理解
转至 http://blog.csdn.net/think_soft/article/details/7998478 使用Camera功能 大多数的Camera功能都是使用Camera.Paramet ...
- Cisco 3550配置DHCP中继代理
实验环境: 1.配置两个VLAN 10 和 VLAN 20 VLAN 10 IP地址设置:192.168.10.1 255.255.255.0 (192.168.10.1是VLAN 10网关 ...
- iOS7以上: 实现如“日历”的 NavigationBar
第一步,隐藏导航栏底部的分割线 如何隐藏导航栏底部的分割线(shadow image/ hairline)? navigationBar.clipsToBounds = YES; //隐藏 navig ...
- 常用Dos(转)
先介绍一下通配符的概念. 通配符*和? *表示一个字符串 ?只代表一个字符 注意通配符只能通配文件名或扩展名,不能全都表示.例如我们要查找以字母y开头的所有文件,可以输入以下命令:dir y*.*:如 ...
- iOS app开发入门
https://github.com/qinjx/30min_guides/blob/master/ios.md
- 虚拟机中安装linux系统步骤
参考:http://blog.csdn.net/u013111221/article/details/50856934 后面参考:http://blog.csdn.net/chenweitang123 ...