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的更多相关文章

  1. 关于WinIO.DLL的键盘输入模拟

    关于WinIO.DLL的键盘输入模拟 最近在找键盘模拟的方式,最后在网上找到了一个WinIO.DLL的IO键盘模拟按键的方式.但是居然那个方法是有问题的.我造了全局的hook监视键盘信息,发现它只是有 ...

  2. win2k,XP下用setupapi.dll自动安装Driver

    win2k,XP下用setupapi.dll自动安装Driver 在驱网看到54cndr 写的这篇文章,虽然自己一直都用Installshield,但还是觉得这个也是一个很好的思路,故摘录在此. 用s ...

  3. 在Delphi中使用键盘勾子获取键盘输入(译--5月7日)

    http://blog.sina.com.cn/s/blog_502b2e970100949s.html 获取键盘输入以控制无法接受输入焦点的控件考虑一些游戏,显示图片在TPainBox,但是TPai ...

  4. pyautogui页面点击和键盘输入

    以下程序实现了在编辑框处点击,然后用键盘输入的功能 import pyautogui import time time.sleep(10) currentMouseX, currentMouseY = ...

  5. C# Winform中无焦点状态下获取键盘输入或者USB扫描枪数据

    类文件: C#类文件 using System; using System.Collections.Generic; using System.Text; using System.Runtime.I ...

  6. 【Visual C++】游戏编程学习笔记之七:键盘输入消息

     本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...

  7. 代码实现:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名

    package com.loaderman.test; import java.io.File; import java.io.FileReader; import java.util.Scanner ...

  8. VB模拟键盘输入的N种方法

    VB模拟键盘输入的N种方法http://bbs.csdn.net/topics/90509805hd378发表于: 2006-12-24 14:35:39用VB模拟键盘事件的N种方法 键盘是我们使用计 ...

  9. 使用C#模拟键盘输入、鼠标移动和点击、设置光标位置及控制应用程序的显示

    1.模拟键盘输入(SendKeys) 功能:将一个或多个按键消息发送到活动窗口,就如同在键盘上进行输入一样. 语法:SendKeys.Send(string keys);SendKeys.SendWa ...

随机推荐

  1. e834. 设置JTabbedPane中卡片的位置

    The tabs of a tabbed pane can be placed on one of the four edges of its container. By default, when ...

  2. 目标检测之2015iccv---objdetection 专题论文

    http://mp7.watson.ibm.com/ICCV2015/ObjectDetectionICCV2015.html

  3. Python——eventlet.wsgi

    eventlet 的 wsgi 模块提供了一种启动事件驱动的WSGI服务器的简洁手段,可以将其作为某个应用的嵌入web服务器,或作为成熟的web服务器,一个这样的web服务器的例子就是 Spawnin ...

  4. OpenStack配置解析库oslo.config的使用方法

    OpenStack的oslo项目旨在独立出系统中可重用的基础功能,oslo.config就是其中一个被广泛使用的库,该项工作的主要目的就是解析OpenStack中命令行(CLI)或配置文件(.conf ...

  5. WebSphere异常:Servlet has become temporarily unavailable for service

    今天发现WebSphere中的一个应用图片看不见了,单独在浏览器中访问图片的路径http://ip地址:9080/应用/--/images/top.jpg,也不显示图片,并且提示异常Servlet h ...

  6. Wings 3D

    Wings 3D 编辑 Wings 3D 是一个开源的三维计算机图形软件.使用翼边数据库.注重于多边形建模,构思取与 Izware 的 Nendo 和 Mirai.支持多种操作系统,包括 Linux. ...

  7. zabbix server端自动发现和zabbix agent端自动注册

    一.zabbix自动发现 利用zabbix的discovery功能可以实现自动批量添加主机的功能. Zabbix自动发现实现自定义主机名: 通过自动发现添加的客户端主机的Host name 是以IP地 ...

  8. 如何找回Ucenter创始人密码,账号无需修改

    UCenter 创始人的密码非常重要,忘记或丢失后,就不能进入 UCenter 进行用户和数据的管理,也会对站点造成安全隐患.由于 UCenter 的密码是采用两次 md5 加一个随机数的形式加密的, ...

  9. 精挑细选 8款HTML5/jQuery应用助网站走向高上大

    在WEB3.0的时代,我们的网站不仅要实现实用价值,更要为用户设计优秀的用户体验.jQuery是一个不错的JS框架,结合目前最新的HTML5技术,我们可以将自己的网站脱胎换骨,立马走向高上大,至少在前 ...

  10. 修改vs2005,vs2008,vs2010调试默认浏览器

    前些日子不小心安装上了一个sogou的浏览器,感觉这个浏览器用起来也算方便,所以就么有卸载,一直就这么用着,但当我用vs来调试web程序的 时候问题出来了,默认的调试浏览器变成了搜狗的浏览器了,我在v ...