[fix] fix left/right modifier key injection while preserving scan code metadata

This commit is contained in:
dijunkun
2026-05-06 17:52:31 +08:00
parent b7a031bb7f
commit b1d956af2c
14 changed files with 354 additions and 75 deletions
+68 -17
View File
@@ -313,10 +313,12 @@ std::string QueryNamedPipeMessage(const std::wstring& pipe_name,
return std::string(buffer, buffer + bytes_read);
}
std::string BuildSecureDesktopKeyboardIpcCommand(int key_code, bool is_down) {
std::string BuildSecureDesktopKeyboardIpcCommand(int key_code, bool is_down,
uint32_t scan_code,
bool extended) {
std::ostringstream stream;
stream << kSecureDesktopKeyboardIpcCommandPrefix << key_code << ":"
<< (is_down ? 1 : 0);
<< (is_down ? 1 : 0) << ":" << scan_code << ":" << (extended ? 1 : 0);
return stream.str();
}
@@ -328,20 +330,27 @@ std::string BuildSecureDesktopMouseIpcCommand(int x, int y, int wheel,
return stream.str();
}
std::string BuildSecureInputHelperKeyboardCommand(int key_code, bool is_down) {
std::string BuildSecureInputHelperKeyboardCommand(int key_code, bool is_down,
uint32_t scan_code,
bool extended) {
std::ostringstream stream;
stream << kCrossDeskSecureInputKeyboardCommandPrefix << key_code << ":"
<< (is_down ? 1 : 0);
<< (is_down ? 1 : 0) << ":" << scan_code << ":" << (extended ? 1 : 0);
return stream.str();
}
bool ParseSecureDesktopKeyboardIpcCommand(const std::string& command,
int* key_code_out,
bool* is_down_out) {
if (key_code_out == nullptr || is_down_out == nullptr) {
int* key_code_out, bool* is_down_out,
uint32_t* scan_code_out,
bool* extended_out) {
if (key_code_out == nullptr || is_down_out == nullptr ||
scan_code_out == nullptr || extended_out == nullptr) {
return false;
}
*scan_code_out = 0;
*extended_out = false;
if (command.rfind(kSecureDesktopKeyboardIpcCommandPrefix, 0) != 0) {
return false;
}
@@ -358,13 +367,46 @@ bool ParseSecureDesktopKeyboardIpcCommand(const std::string& command,
return false;
}
const std::string state = command.substr(separator + 1);
const size_t scan_separator = command.find(':', separator + 1);
const std::string state =
scan_separator == std::string::npos
? command.substr(separator + 1)
: command.substr(separator + 1, scan_separator - separator - 1);
if (state == "1" || state == "down") {
*is_down_out = true;
} else if (state == "0" || state == "up") {
*is_down_out = false;
} else {
return false;
}
if (scan_separator == std::string::npos) {
return true;
}
if (state == "0" || state == "up") {
*is_down_out = false;
const size_t extended_separator = command.find(':', scan_separator + 1);
const std::string scan_code_str =
extended_separator == std::string::npos
? command.substr(scan_separator + 1)
: command.substr(scan_separator + 1,
extended_separator - scan_separator - 1);
try {
*scan_code_out = static_cast<uint32_t>(std::stoul(scan_code_str));
} catch (...) {
return false;
}
if (extended_separator == std::string::npos) {
return true;
}
const std::string extended_str = command.substr(extended_separator + 1);
if (extended_str == "1" || extended_str == "true") {
*extended_out = true;
return true;
}
if (extended_str == "0" || extended_str == "false") {
*extended_out = false;
return true;
}
return false;
@@ -1712,8 +1754,12 @@ std::string CrossDeskServiceHost::HandleIpcCommand(const std::string& command) {
}
int key_code = 0;
bool is_down = false;
if (ParseSecureDesktopKeyboardIpcCommand(normalized, &key_code, &is_down)) {
return SendSecureDesktopKeyboardInput(key_code, is_down);
uint32_t scan_code = 0;
bool extended = false;
if (ParseSecureDesktopKeyboardIpcCommand(normalized, &key_code, &is_down,
&scan_code, &extended)) {
return SendSecureDesktopKeyboardInput(key_code, is_down, scan_code,
extended);
}
return BuildErrorJson("unknown_command");
}
@@ -1928,8 +1974,8 @@ std::string CrossDeskServiceHost::SendSecureAttentionSequence() {
return "{\"ok\":true,\"sent\":\"sas\"}";
}
std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput(int key_code,
bool is_down) {
std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput(
int key_code, bool is_down, uint32_t scan_code, bool extended) {
RefreshSessionState();
ReapSecureInputHelper();
EnsureSessionHelper();
@@ -1963,7 +2009,9 @@ std::string CrossDeskServiceHost::SendSecureDesktopKeyboardInput(int key_code,
return QueryNamedPipeMessage(
GetCrossDeskSecureInputHelperPipeName(target_session_id),
BuildSecureInputHelperKeyboardCommand(key_code, is_down), 1000);
BuildSecureInputHelperKeyboardCommand(key_code, is_down, scan_code,
extended),
1000);
}
bool InstallCrossDeskService(const std::wstring& binary_path) {
@@ -2176,9 +2224,12 @@ std::string QueryCrossDeskService(const std::string& command,
}
std::string SendCrossDeskSecureDesktopKeyInput(int key_code, bool is_down,
uint32_t scan_code,
bool extended,
DWORD timeout_ms) {
return QueryCrossDeskService(
BuildSecureDesktopKeyboardIpcCommand(key_code, is_down), timeout_ms);
return QueryCrossDeskService(BuildSecureDesktopKeyboardIpcCommand(
key_code, is_down, scan_code, extended),
timeout_ms);
}
std::string SendCrossDeskSecureDesktopMouseInput(int x, int y, int wheel,
+6 -1
View File
@@ -9,6 +9,7 @@
#include <Windows.h>
#include <cstdint>
#include <mutex>
#include <string>
#include <thread>
@@ -60,7 +61,9 @@ class CrossDeskServiceHost {
std::string HandleIpcCommand(const std::string& command);
std::string BuildStatusResponse();
std::string SendSecureAttentionSequence();
std::string SendSecureDesktopKeyboardInput(int key_code, bool is_down);
std::string SendSecureDesktopKeyboardInput(int key_code, bool is_down,
uint32_t scan_code = 0,
bool extended = false);
static void WINAPI ServiceMain(DWORD argc, LPWSTR* argv);
static BOOL WINAPI ConsoleControlHandler(DWORD control_type);
@@ -138,6 +141,8 @@ bool StopCrossDeskService(DWORD timeout_ms = 5000);
std::string QueryCrossDeskService(const std::string& command,
DWORD timeout_ms = 1000);
std::string SendCrossDeskSecureDesktopKeyInput(int key_code, bool is_down,
uint32_t scan_code = 0,
bool extended = false,
DWORD timeout_ms = 1000);
std::string SendCrossDeskSecureDesktopMouseInput(int x, int y, int wheel,
int flag,
+96 -15
View File
@@ -485,20 +485,56 @@ bool EnsureThreadDesktop(const wchar_t* desktop_name,
return true;
}
int InjectKeyboardInput(int key_code, bool is_down) {
bool PreferSideSpecificVkInjection(int key_code) {
switch (key_code) {
case VK_LSHIFT:
case VK_RSHIFT:
case VK_LCONTROL:
case VK_RCONTROL:
case VK_LMENU:
case VK_RMENU:
case VK_LWIN:
case VK_RWIN:
return true;
default:
return false;
}
}
int InjectKeyboardInput(int key_code, bool is_down, uint32_t scan_code,
bool extended) {
INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki.wVk = static_cast<WORD>(key_code);
const UINT scan_code =
MapVirtualKeyW(static_cast<UINT>(key_code), MAPVK_VK_TO_VSC_EX);
if (scan_code != 0) {
const bool prefer_vk = PreferSideSpecificVkInjection(key_code);
const UINT resolved_scan_code =
scan_code != 0
? static_cast<UINT>(scan_code & 0xFF) | (extended ? 0xE000u : 0u)
: MapVirtualKeyW(static_cast<UINT>(key_code), MAPVK_VK_TO_VSC_EX);
if (scan_code != 0 && !prefer_vk) {
input.ki.wVk = 0;
input.ki.wScan = static_cast<WORD>(scan_code & 0xFF);
input.ki.dwFlags |= KEYEVENTF_SCANCODE;
if ((scan_code & 0xFF00) != 0) {
if (extended) {
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
}
} else {
input.ki.wVk = static_cast<WORD>(key_code);
if (prefer_vk && resolved_scan_code != 0) {
input.ki.wScan = static_cast<WORD>(resolved_scan_code & 0xFF);
if ((resolved_scan_code & 0xFF00) != 0) {
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
}
} else if (resolved_scan_code != 0) {
input.ki.wVk = 0;
input.ki.wScan = static_cast<WORD>(resolved_scan_code & 0xFF);
input.ki.dwFlags |= KEYEVENTF_SCANCODE;
if ((resolved_scan_code & 0xFF00) != 0) {
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
}
}
}
if (!is_down) {
@@ -514,11 +550,17 @@ int InjectKeyboardInput(int key_code, bool is_down) {
}
bool ParseSecureInputKeyboardCommand(const std::string& command,
int* key_code_out, bool* is_down_out) {
if (key_code_out == nullptr || is_down_out == nullptr) {
int* key_code_out, bool* is_down_out,
uint32_t* scan_code_out,
bool* extended_out) {
if (key_code_out == nullptr || is_down_out == nullptr ||
scan_code_out == nullptr || extended_out == nullptr) {
return false;
}
*scan_code_out = 0;
*extended_out = false;
if (command.rfind(crossdesk::kCrossDeskSecureInputKeyboardCommandPrefix, 0) !=
0) {
return false;
@@ -537,13 +579,46 @@ bool ParseSecureInputKeyboardCommand(const std::string& command,
return false;
}
const std::string state = command.substr(separator + 1);
const size_t scan_separator = command.find(':', separator + 1);
const std::string state =
scan_separator == std::string::npos
? command.substr(separator + 1)
: command.substr(separator + 1, scan_separator - separator - 1);
if (state == "1" || state == "down") {
*is_down_out = true;
} else if (state == "0" || state == "up") {
*is_down_out = false;
} else {
return false;
}
if (scan_separator == std::string::npos) {
return true;
}
if (state == "0" || state == "up") {
*is_down_out = false;
const size_t extended_separator = command.find(':', scan_separator + 1);
const std::string scan_code_str =
extended_separator == std::string::npos
? command.substr(scan_separator + 1)
: command.substr(scan_separator + 1,
extended_separator - scan_separator - 1);
try {
*scan_code_out = static_cast<uint32_t>(std::stoul(scan_code_str));
} catch (...) {
return false;
}
if (extended_separator == std::string::npos) {
return true;
}
const std::string extended_str = command.substr(extended_separator + 1);
if (extended_str == "1" || extended_str == "true") {
*extended_out = true;
return true;
}
if (extended_str == "0" || extended_str == "false") {
*extended_out = false;
return true;
}
return false;
@@ -807,13 +882,17 @@ std::vector<uint8_t> HandleSecureInputHelperCommand(
int key_code = 0;
bool is_down = false;
if (ParseSecureInputKeyboardCommand(command, &key_code, &is_down)) {
const int inject_result = InjectKeyboardInput(key_code, is_down);
uint32_t scan_code = 0;
bool extended = false;
if (ParseSecureInputKeyboardCommand(command, &key_code, &is_down, &scan_code,
&extended)) {
const int inject_result =
InjectKeyboardInput(key_code, is_down, scan_code, extended);
if (inject_result != 0) {
LOG_WARN(
"Secure input helper SendInput failed for key_code={}, is_down={}, "
"err={}",
key_code, is_down, inject_result);
"scan_code={}, extended={}, err={}",
key_code, is_down, scan_code, extended, inject_result);
return BuildTextResponseBytes(BuildErrorJson(
"send_input_failed", static_cast<DWORD>(inject_result)));
}
@@ -823,6 +902,8 @@ std::vector<uint8_t> HandleSecureInputHelperCommand(
json["injected"] = "keyboard";
json["key_code"] = key_code;
json["is_down"] = is_down;
json["scan_code"] = scan_code;
json["extended"] = extended;
json["desktop"] = WideToUtf8(GetCurrentThreadDesktopNameW());
return BuildTextResponseBytes(json.dump());
}