Skip to content

Commit

Permalink
Fix hooking ReadConsoleW in 32 bit CMD.
Browse files Browse the repository at this point in the history
Regression was introduced in clink/dll/shell_cmd.cpp in 304e8a3 on
2015/04/21.

Also fixed how initialization gets triggered, since GetStdHandle doesn't
necessarily occur soon enough.  GetEnvironmentVariableW will always
happen in time because CMD calls it to get the PROMPT string.
  • Loading branch information
chrisant996 committed Oct 21, 2020
1 parent 03320a2 commit 86ced1b
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 15 deletions.
4 changes: 0 additions & 4 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ Here's what's left to do in order to have a reasonable alpha release. The alpha
- Describe the new argmatcher/etc syntax.
- Supply sample inputrc file(s).

## Bugs
- Fixed the 32 bit crash.
- But 32 bit is failing to hook ReadConsoleW, so prompt output doesn't work and input doesn't work correctly.

<br>
<br>

Expand Down
31 changes: 20 additions & 11 deletions clink/app/src/host/host_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,23 +268,24 @@ bool host_cmd::initialise()
hooks.add_iat(base, "SetEnvironmentVariableW", &host_cmd::set_env_var);
hooks.add_iat(base, "WriteConsoleW", &host_cmd::write_console);

// Set a trap to get a callback when cmd.exe fetches stdin handle.
auto get_std_handle = [] (unsigned int handle_id) -> void*
// Set a trap to get a callback when cmd.exe fetches PROMPT environment
// variable. GetEnvironmentVariableW is always called before displaying the
// prompt string, so it's a reliable spot to hook regardless how injection
// is initiated (AutoRun, command line, etc).
auto get_environment_variable_w = [] (LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) -> DWORD
{
seh_scope seh;

void* ret = GetStdHandle(handle_id);
if (handle_id != STD_INPUT_HANDLE)
return ret;
DWORD ret = GetEnvironmentVariableW(lpName, lpBuffer, nSize);

void* base = GetModuleHandle(nullptr);
hook_iat(base, nullptr, "GetStdHandle", funcptr_t(GetStdHandle), 1);
hook_iat(base, nullptr, "GetEnvironmentVariableW", funcptr_t(GetEnvironmentVariableW), 1);

host_cmd::get()->initialise_system();
return ret;
};
auto* as_stdcall = static_cast<void* (__stdcall *)(unsigned)>(get_std_handle);
hooks.add_iat(base, "GetStdHandle", as_stdcall);
auto* as_stdcall = static_cast<DWORD (__stdcall *)(LPCWSTR, LPWSTR, DWORD)>(get_environment_variable_w);
hooks.add_iat(base, "GetEnvironmentVariableW", as_stdcall);

rl_add_funmap_entry("clink-expand-env-var", expand_env_var);
rl_add_funmap_entry("clink-expand-doskey-alias", expand_doskey_alias);
Expand Down Expand Up @@ -482,8 +483,16 @@ BOOL WINAPI host_cmd::set_env_var(const wchar_t* name, const wchar_t* value)
//------------------------------------------------------------------------------
bool host_cmd::initialise_system()
{
// Get the base address of module that exports ReadConsoleW.
void* kernel_module = vm().get_alloc_base(ReadConsoleW);
// Get the base address of module that exports ReadConsoleW. (Can't use the
// address of ReadConsoleW directly, because that's our import library stub,
// not the actual API address.)
HMODULE hlib = LoadLibraryW(L"kernelbase.dll");
if (hlib == nullptr)
return false;
FARPROC proc = GetProcAddress(hlib, "ReadConsoleW");
if (proc == nullptr)
return false;
void* kernel_module = vm().get_alloc_base(proc);
if (kernel_module == nullptr)
return false;

Expand All @@ -505,7 +514,7 @@ bool host_cmd::initialise_system()
m_doskey.add_alias("history", buffer.c_str());
}

// Tag the prompt again just incase it got unset by by something like
// Tag the prompt again just incase it got unset by something like
// setlocal/endlocal in a boot Batch script.
tag_prompt();

Expand Down

0 comments on commit 86ced1b

Please sign in to comment.