zoukankan      html  css  js  c++  java
  • 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 

  • 相关阅读:
    Git 游离态的一次问题解决
    idea每次新建项目的默认路径
    springboot 整合 freemarker
    Linux 学习网站
    springtask 基本使用和 cron 表达式
    volatile 关键字 和 i++ 原子性
    python 自动补全
    nagios维护之常见问题
    nagios维护之添加监控
    windows下python文件与文件夹操作
  • 原文地址:https://www.cnblogs.com/kuangke/p/9525049.html
Copyright © 2011-2022 走看看