Получение раскладки консоли

Тема в разделе "WASM.WIN32", создана пользователем wholegroup, 3 ноя 2008.

  1. wholegroup

    wholegroup New Member

    Публикаций:
    0
    Регистрация:
    3 ноя 2008
    Сообщения:
    2
    Здравствуйте,

    Столкнулся с проблемой получения раскладки клавиатуры чужого консольного приложения в Vista.
    В 2000/XP работает через глобальный хук WH_SHELL с отловом HSHELL_LANGUAGE.
    В висте хук для консольного приложения не вызывается (насколько я понимаю, microsoft не гарантировал работу хука для консольных приложений даже в 2k/XP).
    Смотрел "известные" исходники программы internat, там сделано через хук.
    Подскажите пожалуйста, какие еще существуют варианты узнать раскладку чужого консольного приложения?

    Спасибо.
     
  2. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    В консольной подсистеме есть недокументированный сервис ConsolepGetLangId, для XPSP2 его код 0x20251. Соответствующая апи есть GetConsoleLangId(), но не экспортируется и вызывается она при аттаче к консоле из kernel32!AttachConsole. Структура сообщения:
    Код (Text):
    1. typedef struct _CONSOLE_LANGID_MSG {
    2.     IN HANDLE ConsoleHandle;
    3.     OUT LANGID LangId;
    4. } CONSOLE_LANGID_MSG, *PCONSOLE_LANGID_MSG;
    5.  
    6. typedef struct _CONSOLE_API_MSG {
    7.     PORT_MESSAGE h;
    8.     PCSR_CAPTURE_HEADER CaptureBuffer;
    9.     CSR_API_NUMBER ApiNumber;
    10.     ULONG ReturnValue;
    11.     ULONG Reserved;
    12.     CONSOLE_LANGID_MSG GetConsoleLangId;
    13. } CONSOLE_API_MSG, *PCONSOLE_API_MSG;
    Следует просто вызвать CsrClientCallServer(), передав указатель на CONSOLE_API_MSG, указав ApiNumber = ConsolepGetLangId и GetConsoleLangId.ConsoleHandle, последний получается аттачем к консоле. В висте номер сервиса наверно другой.
     
  3. wholegroup

    wholegroup New Member

    Публикаций:
    0
    Регистрация:
    3 ноя 2008
    Сообщения:
    2
    Спасибо за отличную идею!

    Код, который компилируется, написал ;)
    На данном этапе CsrClientCallServer после вызова возвращает ReturnValue = 0xc0000008. Тестировал на русской XPSP2
    Возникло еще пару вопросов.

    1. Я правильно понимаю, что ConsoleHandle активного окна получать нужно так:

    DWORD dwProcessId;
    GetWindowThreadProcessId(GetForegroundWindow(), &dwProcessId);

    AttachConsole(dwProcessId);
    HANDLE hConsoleHandle = GetStdHandle(STD_INPUT_HANDLE);

    ? или имеется ввиду какой-то другой дескриптор?

    2. Можно как-то программно узнавать код ConsolepGetLangId ? И в целом, какая методика определения этого ApiNumber ?

    Большое спасибо.
     
  4. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Возвращает STATUS_INVALID_HANDLE.
    Код из сорсов винды:
    Код (Text):
    1. #define GET_CONSOLE_HANDLE ((NtCurrentPeb())->ProcessParameters->ConsoleHandle)
    2. BOOL
    3. WINAPI
    4. GetConsoleLangId(
    5.     OUT LANGID *lpLangId
    6.     )
    7.  
    8. /*++
    9.  
    10. Parameters:
    11.  
    12.     lpLangId - Supplies a pointer to a LANGID in which to store the Language ID.
    13.  
    14. Return Value:
    15.  
    16.     TRUE - The operation was successful.
    17.  
    18.     FALSE/NULL - The operation failed. Extended error status is available
    19.         using GetLastError.
    20.  
    21.  
    22. --*/
    23.  
    24. {
    25.     CONSOLE_API_MSG m;
    26.     PCONSOLE_LANGID_MSG a = &m.u.GetConsoleLangId;
    27.  
    28.     a->ConsoleHandle = GET_CONSOLE_HANDLE;
    29.     CsrClientCallServer( (PCSR_API_MSG)&m,
    30.                          NULL,
    31.                          CSR_MAKE_API_NUMBER( CONSRV_SERVERDLL_INDEX,
    32.                                               ConsolepGetLangId
    33.                                             ),
    34.                          sizeof( *a )
    35.                        );
    36.     if (NT_SUCCESS( m.ReturnValue )) {
    37.         try {
    38.             *lpLangId = a->LangId;
    39.         } except( EXCEPTION_EXECUTE_HANDLER ) {
    40.             return FALSE;
    41.         }
    42.         return TRUE;
    43.     } else {
    44.         return FALSE;
    45.     }
    46. }
    Хэндл консоли - это значение из RTL_USER_PROCESS_PARAMETERS.ConsoleHandle.
    В XPSP2 одна ссылка в kernel32 на GetConsoleLangId, из SetTEBLangID(), эта также не экспортируется, но она сохраняет значение:
    Код (Text):
    1. VOID
    2. SetTEBLangID(
    3.     VOID
    4.     )
    5.  
    6. /*++
    7.  
    8.     Sets the Language Id in the TEB to Far East if code page CP are
    9.     Japanese/Korean/Chinese.  This is done in order for FormatMessage
    10.     to display any Far East character when cmd is running in its code page.
    11.     All messages displayed in non-FE code page will be displayed in English.
    12.  
    13. --*/
    14.  
    15. {
    16.     LANGID LangId;
    17.  
    18.     if (GetConsoleLangId(&LangId)) {
    19.         SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
    20.     }
    21. }
    Можно попробовать вызвать GetThreadLocale() после аттача к консоли, либо что тоже, считать из TEB поле CurrentLocale.
    Ссылки на SetTEBLangID() четыре. Наиболее прост вызов:
    Код (Text):
    1. BOOL
    2. WINAPI
    3. SetConsoleOutputCP(
    4.     IN UINT wCodePageID
    5.     )
    6.  
    7. /**++
    8.  
    9. Parameters:
    10.  
    11.     wCodePageID - the code page is to set for the current console output.
    12.  
    13. Return Value:
    14.  
    15.     TRUE - The operation was successful.
    16.  
    17.     FALSE/NULL - The operation failed. Extended error status is available
    18.         using GetLastError.
    19.  
    20.  
    21. --*/
    22.  
    23. {
    24.  
    25.     NTSTATUS Status;
    26.    
    27.     Status = SetConsoleOutputCPInternal(wCodePageID);
    28.    
    29.     if(NT_SUCCESS(Status)) {
    30.         SetTEBLangID();
    31.         return TRUE;
    32.     }
    33.     else {
    34.         SET_LAST_NT_ERROR (Status);
    35.         return FALSE;
    36.     }
    37. }
    Смотри, в сорсах всё есть, либо под дизасмом.
    Получить код сервиса можно двумя способами.
    1. Пройтись дизасмом длин, раскрыв два вложенных вызова процедуры(SetConsoleOutputCP->SetTEBLangID->GetConsoleLangId), из последней функции извлечь код.
    2. Отследить запрос к серверу(CsrClientCallServer). Тут несколько вариантов:
    > Перехватить CsrClientCallServer() и считать параметры при вызове.
    > Трассировать процедуру, при вызове CsrClientCallServer() считать параметры.
    > Трассировать AttachConsole() и обрабатывать стековые фреймы, дабы отследить последовательность вызова.
    > Отследить SetThreadLocale(), либо запись в TEB.CurrentLocale
    Все способы просто реализуются.
    GetStdHandle возвращает значение поля RTL_USER_PROCESS_PARAMETERS.StandardOutput, а нужно поле ConsoleHandle. Считай вручную.