Many games or 3D simulation software use directinput as the input interface to better support peripherals. So how can we achieve this by simulating a mouse or keyboard to control the automatic operation of a game or 3D software?
I studied the keyboard section. And so on.
Start module: dinput8.dll
Software used: idapro5.0, ollydbg, c32asm
The idea is as follows. There is a loop query status processing during dinput programming. The called function is ckbd_getdevicestate (this function address can be found from the analysis result of idapro5.0)
The call code is similar:
Hresult updateinputstate (void)
{
Dword I;
If (lpkeyboard! = NULL)
{
Dideviceobjectdata didod [dinput_buffersize]; // calls es buffered data
DWORD dwelements;
Hresult hr;
HR = dierr_inputlost;
While (HR! = Di_ OK)
{
Dwelements = dinput_buffersize;
HR = lpkeyboard-> getdevicedata (sizeof (dideviceobjectdata), didod, & dwelements, 0); Call ckbd_getdevicestate here
If (HR! = Di_ OK)
{
HR = lpkeyboard-> acquire ();
If (failed (HR ))
Return hr;
}
}
If (failed (HR ))
Return hr;
}
For (INT I = 0; I <dwelements; I ++)
{
// Put the processing code here
// Didod [I]. dwofs indicates that the key is pressed or released.
// Didod [I]. dwdata records the state of the key. The highest bit of low byte is 1, which indicates that the key is pressed, and 0 indicates that the key is released.
// Generally, didod [I]. dwdata & 0x80 is used for testing.
}
Return s_ OK;
}
How is the keyboard status obtained? See the following IDA analysis results.
. 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]; based on tracking and analysis. The memory pointed to by ESI is a keyboard status table with different key bits. If you press 0x80, if you do not press it, It is 00.
. Text: 6d18c5fe push EDI
. Text: 6d18c5ff mov EDI, [EBP + arg_4]
. Text: 6d18c602 push 40 h; copy length: 0x100 bytes 40 h * 4
. Text: 6d18c604 pop ECx
. Text: 6d18c605 rep movsd; copy the keyboard status to the external receiving buffer
. 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
We learned from the trace that the buffer is stored in a global variable memory area. The corresponding key bits of the keyboard table are listed below.
The base address is 6d1a4448, which can be obtained by tracking the content of ESI.
6d1a4448 H + 2 = 1 to 6d1a4448 H + bH = 0
6d1a4448 H + CH =-
6d1a4448 H + DH =
6d1a4448 H + 1eh =
6d1a4448 H + 30 h = B
6d1a4448 H + 2EH = C
6d1a4448 H + 20 h = d
6d1a4448 H + 12 h = E
6d1a4448 H + 21 H = f
6d1a4448 H + 22 h = G
6d1a4448 H + 23 h = H
6d1a4448 H + 17 h = I
6d1a4448 H + 24 h = J
6d1a4448 H + 25 h = K
6d1a4448 H + 26 h = L
6d1a4448 H + 32 h = m
6d1a4448 H + 31 H = N
6d1a4448 H + 18 h = o
6d1a4448 H + 19 h = P
6d1a4448 H + 10 h = Q
6d1a4448 H + 13 H = r
6d1a4448 H + 1fh = s
6d1a4448 H + 14 h = T
6d1a4448 H + 16 h = u
6d1a4448 H + 2fh = V
6d1a4448 H + 11 h = W
6d1a4448 H + 2dh = x
6d1a4448 H + 15 h = y
6d1a4448 H + 2ch = z
6d1a4448 H + 1ch = enter
6d1a4448 H + c8h = up
6d1a4448 H + d0h = down
6d1a4448 H + cbh = left
6d1a4448 H + CDH = right
So how to track debugging in ollydbg.
We can use c32asm to modify ckbd_getdevicestate 6d1880a7 8bff mov EDI and EDI;
The machine code of the first byte of the function is CC, that is, the INT 3 breakpoint.
Currently, it is 8bff and changed to CCFF. When this function is executed, the system will prompt you to find the debugging Location Error 0x80000003. Go to the debugging page as prompted.
First, you must set ollydbg as the default debugger.
Will stop in 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 modify CC to 8b
Step by step F8 to this sentence.
. Text: 6d18c5fb mov ESI, [eax + 4]
Press F8.
ESI has an address.
View the content pointed to by ESI. We can see that all parts are 0. Here is the keyboard buffer.
Since the keyboard buffer address is fixed. We can simulate the encoding.
The first thing to do is to insert our code into the process to be modified. Hook or remote thread. A lot of methods are not mentioned.
I am using a keyboard hook.
Byte keymap [0x100] = {null };
Byte * dinput8keymap = (byte *) 0x24448; // address offset in the keyboard ing area
// Calculate the relative position of the buffer at the initialization of the program
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 );
}
// First define our key table to write an initialization Function
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;
}
// Compile a function for setting the keyboard to 80
Void setkeydown (byte VK)
{
// Case-sensitive Conversion
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;
}
}
}
}
// This function is lazy. If the keys are popped up, we will clear all the values 0.
Void setkeyup (byte VK)
{
Zeromemory (dinput8keymap, 0x100 );
}
// My keyboard hook. Implemented in this way
Lresult callback keyboardproc (INT ncode, wparam, lparam)
{
If (lparam = 0xc0000001)
{
Setkeydown (byte) wparam );
}
If (lparam = 1)
{
Setkeyup (byte) wparam );
}
Return callnexthookex (winkbd, ncode, wparam, lparam );
}
This completes. The program can be automatically controlled. Isn't it interesting?
Development Environment:
Vs2005, XP SP2