2012-07-31 76 views
0

我在這裏沒有想法。我有一段代碼改編自http://thetechnofreak.com/technofreak/keylogger-visual-c/將鍵碼轉換爲Unicode字符。除非您嘗試從64位Windows運行32位版本,否則在任何情況下都可以正常工作。出於某種原因,pKbd-> pVkToWcharTable保持返回NULL。我已經嘗試__ptr64以及爲kbd dll路徑明確指定SysWOW64和System32。我在互聯網上發現了幾個指向這個確切或非常類似的問題的項目,但我似乎無法獲得任何解決方案(請參閱:KbdLayerDescriptor returns NULL at 64bit architecture)以下是我在Windows XP上使用mingw-32編譯的測試代碼( gcc -std = c99 Wow64Test.c),然後在Windows 7 64位上執行。在Windows XP上,我得到一個有效的指針,但是在Windows 7上,我得到了NULL。KbdLayerDescriptor pvkToWcharTable在Win64上返回NULL

***更新:所以看起來像我遇到的問題是由於mingw沒有正確實現__ptr64,因爲sizeof操作給出了4個字節,而不是由visual studio給出的8個字節。所以真正的解決方案是找出一種方法來使KBD_LONG_POINTER的大小變爲動態或至少64位,但我不確定這是否可能。有任何想法嗎?

#include <windows.h> 
#include <stdio.h> 

#define KBD_LONG_POINTER __ptr64 
//#define KBD_LONG_POINTER 

typedef struct { 
    BYTE Vk; 
    BYTE ModBits; 
} VK_TO_BIT, *KBD_LONG_POINTER PVK_TO_BIT; 

typedef struct { 
    PVK_TO_BIT pVkToBit; 
    WORD wMaxModBits; 
    BYTE ModNumber[]; 
} MODIFIERS, *KBD_LONG_POINTER PMODIFIERS; 

typedef struct _VK_TO_WCHARS1 { 
    BYTE VirtualKey; 
    BYTE Attributes; 
    WCHAR wch[1]; 
} VK_TO_WCHARS1, *KBD_LONG_POINTER PVK_TO_WCHARS1; 

typedef struct _VK_TO_WCHAR_TABLE { 
    PVK_TO_WCHARS1 pVkToWchars; 
    BYTE nModifications; 
    BYTE cbSize; 
} VK_TO_WCHAR_TABLE, *KBD_LONG_POINTER PVK_TO_WCHAR_TABLE; 

typedef struct { 
    DWORD dwBoth; 
    WCHAR wchComposed; 
    USHORT uFlags; 
} DEADKEY, *KBD_LONG_POINTER PDEADKEY; 

typedef struct { 
    BYTE vsc; 
    WCHAR *KBD_LONG_POINTER pwsz; 
} VSC_LPWSTR, *KBD_LONG_POINTER PVSC_LPWSTR; 

typedef struct _VSC_VK { 
    BYTE Vsc; 
    USHORT Vk; 
} VSC_VK, *KBD_LONG_POINTER PVSC_VK; 

typedef struct _LIGATURE1 { 
    BYTE VirtualKey; 
    WORD ModificationNumber; 
    WCHAR wch[1]; 
} LIGATURE1, *KBD_LONG_POINTER PLIGATURE1; 

typedef struct tagKbdLayer { 
    PMODIFIERS pCharModifiers; 
    PVK_TO_WCHAR_TABLE pVkToWcharTable; 
    PDEADKEY pDeadKey; 
    PVSC_LPWSTR pKeyNames; 
    PVSC_LPWSTR pKeyNamesExt; 
    WCHAR *KBD_LONG_POINTER *KBD_LONG_POINTER pKeyNamesDead; 
    USHORT *KBD_LONG_POINTER pusVSCtoVK; 
    BYTE bMaxVSCtoVK; 
    PVSC_VK pVSCtoVK_E0; 
    PVSC_VK pVSCtoVK_E1; 
    DWORD fLocaleFlags; 
    BYTE nLgMax; 
    BYTE cbLgEntry; 
    PLIGATURE1 pLigature; 
    DWORD dwType; 
    DWORD dwSubType; 
} KBDTABLES, *KBD_LONG_POINTER PKBDTABLES; 


typedef PKBDTABLES(CALLBACK *KbdLayerDescriptor) (VOID); 

int main() { 
    PKBDTABLES pKbd; 
    HINSTANCE kbdLibrary = NULL; 

    //"C:\\WINDOWS\\SysWOW64\\KBDUS.DLL" 
    //"C:\\WINDOWS\\System32\\KBDUS.DLL" 
    kbdLibrary = LoadLibrary("C:\\WINDOWS\\SysWOW64\\KBDUS.DLL"); 

    KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(kbdLibrary, "KbdLayerDescriptor"); 

    if(pKbdLayerDescriptor != NULL) { 
     pKbd = pKbdLayerDescriptor(); 

     printf("Is Null? %d 0x%X\n", sizeof(pKbd->pVkToWcharTable), pKbd->pVkToWcharTable); 
    } 

    FreeLibrary(kbdLibrary); 
    kbdLibrary = NULL; 
} 

回答

3

對您來說可能會遲到,但對於有同樣問題的人來說,這是一個解決方案。該演示和不完整的解釋會有所幫助,但只有在Visual Studio中的工作原理: http://www.codeproject.com/Articles/439275/Loading-keyboard-layout-KbdLayerDescriptor-in-32-6

在結構kbd.h所有的指針都具有KBD_LONG_POINTER宏,它被定義爲*__ptr64*在64位操作系統。在Visual Studio中,這會使指針佔用8個字節,而不是通常的32位程序中的4個。不幸的是,在MinGW中,*__ptr64*被定義爲不做任何事情。

正如鏈接說明中所寫,KbdLayerDescriptor函數在32位和64位Windows上返回指針不同。指針的大小似乎取決於操作系統而不是正在運行的程序。實際上,對於32位程序,指針在64位操作系統上仍然是4個字節,但在VS中,關鍵字在於它們不是。

例如一些結構看起來像這樣在kbd.h

typedef struct { 
    BYTE Vk; 
    BYTE ModBits; 
} VK_TO_BIT, *KBD_LONG_POINTER PVK_TO_BIT; 

typedef struct { 
    PVK_TO_BIT pVkToBit; 
    WORD wMaxModBits; 
    BYTE ModNumber[]; 
} MODIFIERS, *KBD_LONG_POINTER PMODIFIERS; 

這不能既不MinGW的,也沒有在VS上64位Windows的32個程序的工作。因爲MODIFIERS中的pVkToBit成員只有4個字節沒有__ptr64。解決辦法是忘掉KBD_LONG_POINTER(你甚至可以將它們全部刪除)並定義與上述類似的結構。即:

struct VK_TO_BIT64 
{ 
    BYTE Vk; 
    BYTE ModBits; 
}; 

struct MODIFIERS64 
{ 
    VK_TO_BIT64 *pVkToBit; 
    int _align1; 
    WORD wMaxModBits; 
    BYTE ModNumber[]; 
}; 

(你可以使用VK_TO_BIT,而不是定義自己VK_TO_BIT64,因爲它們是相同的,但有不同的定義有助於瞭解發生了什麼事情。)

成員pVkToBit仍然佔用4字節,但KbdLayerDescriptor在64位操作系統上填充指向8字節的指針,所以我們必須插入一些填充(int _align1)。

您必須對kbd.h中的其他結構做同樣的事情。例如,這將取代KBDTABLES

struct WCHARARRAY64 
{ 
    WCHAR *str; 
    int _align1; 
}; 

struct KBDTABLES64 
{ 
    MODIFIERS64 *pCharModifiers; 
    int _align1; 
    VK_TO_WCHAR_TABLE64 *pVkToWcharTable; 
    int _align2; 
    DEADKEY64 *pDeadKey; 
    int _align3; 
    VSC_LPWSTR64 *pKeyNames; 
    int _align4; 
    VSC_LPWSTR64 *pKeyNamesExt; 
    int _align5; 
    WCHARARRAY64 *pKeyNamesDead; 
    int _align6; 
    USHORT *pusVSCtoVK; 
    int _align7; 
    BYTE bMaxVSCtoVK; 
    int _align8; 
    VSC_VK64 *pVSCtoVK_E0; 
    int _align9; 
    VSC_VK64 *pVSCtoVK_E1; 
    int _align10; 
    DWORD fLocaleFlags; 
    byte nLgMax; 
    byte cbLgEntry; 
    LIGATURE64_1 *pLigature; 
    int _align11; 
    DWORD dwType; 
    DWORD dwSubType; 
}; 

(請注意,_align8成員不來的指針後)。

對所有使用這個,你必須檢查你是否在64位Windows與運行這個:http://msdn.microsoft.com/en-us/library/ms684139%28v=vs.85%29.aspx

如果不是,請使用kbd.h的原始結構,因爲這些指針的行爲是正確的。他們佔用4個字節。如果程序在64位操作系統上運行,請使用您創建的結構。您可以使用此實現它:

typedef __int64 (CALLBACK *LayerDescriptor64)(); // Result should be cast to KBDTABLES64. 
typedef PKBDTABLES (CALLBACK *LayerDescriptor)(); // This is used on 32 bit OS. 

static PKBDTABLES kbdtables = NULL; 
static KBDTABLES64 *kbdtables64 = NULL; 

而且在一些初始化函數:

if (WindowsIs64Bit()) // Your function that checks the OS version. 
{ 
    LayerDescriptor64 KbdLayerDescriptor = (LayerDescriptor64)GetProcAddress(kbdLibrary, "KbdLayerDescriptor"); 
    if (KbdLayerDescriptor != NULL) 
     kbdtables64 = (KBDTABLES64*)KbdLayerDescriptor(); 
    else 
     kbdtables64 = NULL; 
} 
else 
{ 
    LayerDescriptor KbdLayerDescriptor = (LayerDescriptor)GetProcAddress(kbdLibrary, "KbdLayerDescriptor"); 
    if (KbdLayerDescriptor != NULL) 
     kbdtables = KbdLayerDescriptor(); 
    else 
     kbdtables = NULL; 
} 

此解決方案不使用__ptr64可言的,無論是在VS和MinGW工作。你必須注意的事情是:

  1. 結構應該在8個字節的邊界上對齊。 (這是在當前VS或MinGW的,至少對於C默認++。)
  2. 不要定義KBD_LONG_POINTER__ptr64,或其他地方將其刪除。雖然你最好不要改變kbd.h.
  3. 瞭解結構成員的對齊方式。 (我將它編譯爲C++而不是C,我不確定C的對齊規則是否會有任何不同。)
  4. 根據操作系統使用正確的變量(kbdtableskbdtables64)。
  5. 編譯64位程序時顯然不需要此解決方案。
+0

首先,感謝您花時間解釋所有這些!我最終採取了類似但更具動態性,並且隨後更復雜的途徑來完成相同的結果。基本上,我基於簡單的Wow64檢查計算了運行時指針填充。源代碼在公共領域完全可用:https://github.com/kwhat/libuiohook/blob/master/src/windows/input_helper.c – 2014-04-18 19:45:35