From 816e8e6421d5ef968b73c74830d7353cc1a29656 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 24 Jun 2020 01:32:56 +0800 Subject: [PATCH] fix: log: switch from WriteFile(StdHandle) to write(1/2) under Cygwin, resolving Cygwin UTF-8 output encoding issue --- README.md | 6 +- cygwin_build/Makefile | 4 +- cygwin_build/test/fork_exec/test5.c | 4 ++ include/hookdll_util_generic.h | 5 +- .../proxychains_hook.dll.vcxproj | 6 +- .../proxychains_hook.dll.vcxproj.filters | 6 +- ...age.c => hookdll_util_ipc_message_win32.c} | 2 +- src/dll/hookdll_util_log_cygwin.c | 31 +++++++++++ ...ookdll_util.c => hookdll_util_log_win32.c} | 55 ++++++++++++++++++- ...til_wsock.c => hookdll_util_wsock_win32.c} | 2 +- src/exe/main.c | 34 ++++++++---- test/test.cpp | 2 + 12 files changed, 127 insertions(+), 30 deletions(-) rename src/dll/{hookdll_util_ipc_message.c => hookdll_util_ipc_message_win32.c} (99%) create mode 100644 src/dll/hookdll_util_log_cygwin.c rename src/dll/{hookdll_util.c => hookdll_util_log_win32.c} (82%) rename src/dll/{hookdll_util_wsock.c => hookdll_util_wsock_win32.c} (99%) diff --git a/README.md b/README.md index 65259af..3a333b5 100644 --- a/README.md +++ b/README.md @@ -150,17 +150,13 @@ its descendant processes to do `SetThreadContext()`. This means proxychains.exe is in no way compatible with terminals based on ConEmu (like cmder). -## Encoding Issue Regarding Cygwin and Mintty - -Cygwin winsup [hooks](https://github.com/mirror/newlib-cygwin/blob/a97bdf100f54c736c1ab46d39984c4deaacd7386/winsup/cygwin/fhandler_tty.cc#L229) `WriteFile` and some other functions, forcing current process to switch to pseudo console etc. Proxychains.exe uses `WriteFile()` for log output, so encoding issue may happen (like UTF-8 Chinese characters displayed as "???"). - ## To-do -- [ ] Resolve encoding issue regarding Cygwin and Mintty - [ ] Properly handle "fork-and-exit" child process ? (In this case the descendant processes' dns queries would never succeed) - [ ] Remote DNS resolving based on UDP associate - [ ] Hook `sendto()`, coping with applications which do TCP fast open +- [X] Resolve encoding issue regarding Cygwin and Mintty (fixed in 0.6.7) - [X] Fake IPs should be filtered according to types of resolved IPs and hints in `GetAddrInfoW` and `gethostbyname`, otherwise crash may happen (fixed in 0.6.7) diff --git a/cygwin_build/Makefile b/cygwin_build/Makefile index 9544155..77a6e9b 100644 --- a/cygwin_build/Makefile +++ b/cygwin_build/Makefile @@ -164,10 +164,10 @@ MADE_REMOTE_FUNC_BIN_HEADER : proxychains_helper.o $(SRC_PREFIX)remote_function. $(MAKE) -f $(MAKEFILE_PATH) $(REMOTE_FUNC_BIN_HEADER) touch MADE_REMOTE_FUNC_BIN_HEADER -$(DLL_PATH_DEBUG) : ../minhook/libMinHook.a dll/hookdll_main.o dll/hook_connect_win32.o dll/hook_connect_win32.o dll/hook_connect_cygwin.o dll/hook_createprocess_win32.o dll/hook_installer.o dll/ipc_client_and_child_data.o dll/hookdll_util.o dll/hookdll_util_wsock.o dll/hookdll_util_ipc_message.o cygwin_strsafe/strsafe.o +$(DLL_PATH_DEBUG) : ../minhook/libMinHook.a dll/hookdll_main.o dll/hook_connect_win32.o dll/hook_connect_win32.o dll/hook_connect_cygwin.o dll/hook_createprocess_win32.o dll/hook_installer.o dll/ipc_client_and_child_data.o dll/hookdll_util_log_win32.o dll/hookdll_util_log_cygwin.o dll/hookdll_util_wsock_win32.o dll/hookdll_util_ipc_message_win32.o cygwin_strsafe/strsafe.o $(CC) -shared -o$@ -Wl,--out-implib=$(DLL_IMPLIB_PATH_DEBUG),--output-def,$@.def $(LIB_DIR_LDFLAGS) $(filter %.o %.a %.lib,$^) -Wl,-static -lMinHook -Wl,-Bdynamic -lntdllcrt -lws2_32 -lshlwapi -$(DLL_PATH_RELEASE) : ../minhook/libMinHook.a dll/hookdll_main.o dll/hook_connect_win32.o dll/hook_connect_win32.o dll/hook_connect_cygwin.o dll/hook_createprocess_win32.o dll/hook_installer.o dll/ipc_client_and_child_data.o dll/hookdll_util.o dll/hookdll_util_wsock.o dll/hookdll_util_ipc_message.o cygwin_strsafe/strsafe.o +$(DLL_PATH_RELEASE) : ../minhook/libMinHook.a dll/hookdll_main.o dll/hook_connect_win32.o dll/hook_connect_win32.o dll/hook_connect_cygwin.o dll/hook_createprocess_win32.o dll/hook_installer.o dll/ipc_client_and_child_data.o dll/hookdll_util_log_win32.o dll/hookdll_util_log_cygwin.o dll/hookdll_util_wsock_win32.o dll/hookdll_util_ipc_message_win32.o cygwin_strsafe/strsafe.o $(CC) -shared -o$@ -Wl,--out-implib=$(DLL_IMPLIB_PATH_RELEASE),--output-def,$@.def $(LIB_DIR_LDFLAGS) $(filter %.o %.a %.lib,$^) -Wl,-static -lMinHook -Wl,-Bdynamic -lntdllcrt -lws2_32 -lshlwapi # -Wl,--export-all-symbols \ diff --git a/cygwin_build/test/fork_exec/test5.c b/cygwin_build/test/fork_exec/test5.c index 35c7dc9..d90a46c 100644 --- a/cygwin_build/test/fork_exec/test5.c +++ b/cygwin_build/test/fork_exec/test5.c @@ -17,12 +17,16 @@ int main(int argc, char*const*const argv, char*const*const envp) pid_t child_pid; int ret; char *const argv_command[] = { "/bin/echo", "你好" }; + BOOL (WINAPI *WriteFunc)(HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED); //setlocale(LC_ALL, ""); printf("哈啰\n"); DWORD cbWritten; + // WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "a", 1, &cbWritten, NULL); + WriteFunc = (void*)0x7FF8A2212500; + WriteFunc(GetStdHandle(STD_OUTPUT_HANDLE), "z", 1, &cbWritten, NULL); FlushFileBuffers(GetStdHandle(STD_OUTPUT_HANDLE)); // WriteFile(GetStdHandle(STD_ERROR_HANDLE), "b", 1, &cbWritten, NULL); // FlushFileBuffers(GetStdHandle(STD_ERROR_HANDLE)); diff --git a/include/hookdll_util_generic.h b/include/hookdll_util_generic.h index e4e2a17..6551278 100644 --- a/include/hookdll_util_generic.h +++ b/include/hookdll_util_generic.h @@ -22,8 +22,9 @@ #include "tls_generic.h" #include "hookdll_util_ipc_win32.h" +PXCH_DLL_API extern HANDLE g_hCygwinConsoleSemaphore; extern PXCH_INJECT_REMOTE_DATA* g_pRemoteData; -PXCH_DLL_API extern const wchar_t* g_szRuleTargetDesc[3]; +PXCH_DLL_API extern const wchar_t* g_szRuleTargetDesc[3]; // *_early are per-process instead of per-thread, which will cause race condition, and are only used at early stages of DLL loading and hook initializing PXCH_DLL_API extern wchar_t g_szDumpMemoryBuf_early[PXCH_MAX_DUMP_MEMORY_BUFSIZE]; @@ -40,10 +41,10 @@ PXCH_DLL_API extern char g_szFwprintfBuf_early[PXCH_MAX_FWPRINTF_BUFSIZE]; #define g_szFwprintfWbuf ((g_dwTlsIndex != TLS_OUT_OF_INDEXES) ? PXCH_TLS_PTR_FORMAT_FWPRINTF_W_BUF(g_dwTlsIndex) : g_szFwprintfWbuf_early) #define g_szFwprintfBuf ((g_dwTlsIndex != TLS_OUT_OF_INDEXES) ? PXCH_TLS_PTR_FORMAT_FWPRINTF_BUF(g_dwTlsIndex) : g_szFwprintfBuf_early) - PXCH_DLL_API const wchar_t* FormatHostPortToStr(const void* pHostPort, int iAddrLen); PXCH_DLL_API const wchar_t* DumpMemory(const void* p, int iLength); PXCH_DLL_API void IndexToIp(const PROXYCHAINS_CONFIG* pPxchConfig, PXCH_IP_ADDRESS* pIp, PXCH_UINT32 iIndex); PXCH_DLL_API void IpToIndex(const PROXYCHAINS_CONFIG* pPxchConfig, PXCH_UINT32* piIndex, const PXCH_IP_ADDRESS* pIp); +void pxch_cygwin_write(int fd, const void *buf, size_t nbyte); \ No newline at end of file diff --git a/proxychains_hook.dll/proxychains_hook.dll.vcxproj b/proxychains_hook.dll/proxychains_hook.dll.vcxproj index f4abcee..f9858f5 100644 --- a/proxychains_hook.dll/proxychains_hook.dll.vcxproj +++ b/proxychains_hook.dll/proxychains_hook.dll.vcxproj @@ -212,9 +212,9 @@ - - - + + + diff --git a/proxychains_hook.dll/proxychains_hook.dll.vcxproj.filters b/proxychains_hook.dll/proxychains_hook.dll.vcxproj.filters index ae578e7..3d3bc64 100644 --- a/proxychains_hook.dll/proxychains_hook.dll.vcxproj.filters +++ b/proxychains_hook.dll/proxychains_hook.dll.vcxproj.filters @@ -33,13 +33,13 @@ Sources - + Sources - + Sources - + Sources diff --git a/src/dll/hookdll_util_ipc_message.c b/src/dll/hookdll_util_ipc_message_win32.c similarity index 99% rename from src/dll/hookdll_util_ipc_message.c rename to src/dll/hookdll_util_ipc_message_win32.c index 373d43e..8e6d081 100644 --- a/src/dll/hookdll_util_ipc_message.c +++ b/src/dll/hookdll_util_ipc_message_win32.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* hookdll_util_ipc_message.c +/* hookdll_util_ipc_message_win32.c * Copyright (C) 2020 Feng Shun. * * This program is free software: you can redistribute it and/or modify diff --git a/src/dll/hookdll_util_log_cygwin.c b/src/dll/hookdll_util_log_cygwin.c new file mode 100644 index 0000000..cddd64d --- /dev/null +++ b/src/dll/hookdll_util_log_cygwin.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* hookdll_util_log_cygwin.c + * Copyright (C) 2020 Feng Shun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program. If not, see + * . + */ +#include "includes_generic.h" +#include "defines_generic.h" +#include + +#include "log_generic.h" +#include "tls_generic.h" +#include "hookdll_generic.h" +#include "hookdll_util_generic.h" + +void pxch_cygwin_write(int fd, const void *buf, size_t nbyte) +{ + write(fd, buf, nbyte); +} \ No newline at end of file diff --git a/src/dll/hookdll_util.c b/src/dll/hookdll_util_log_win32.c similarity index 82% rename from src/dll/hookdll_util.c rename to src/dll/hookdll_util_log_win32.c index e456751..da1a332 100644 --- a/src/dll/hookdll_util.c +++ b/src/dll/hookdll_util_log_win32.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* hookdll_util.c +/* hookdll_util_log_win32.c * Copyright (C) 2020 Feng Shun. * * This program is free software: you can redistribute it and/or modify @@ -21,6 +21,8 @@ #include "hookdll_generic.h" #include "hookdll_util_win32.h" +HANDLE g_hCygwinConsoleSemaphore; + PXCH_DLL_API PXCH_UINT32 g_dwTlsIndex = TLS_OUT_OF_INDEXES; PXCH_DLL_API const PXCH_UINT32 g_dwW32SystemTimeSize = sizeof(SYSTEMTIME); @@ -117,10 +119,12 @@ void pxchlog_ipc_func(const wchar_t* prefix_fmt, const wchar_t* ipc_prefix_fmt, PXCH_DLL_API void StdVwprintf(DWORD dwStdHandle, const WCHAR* fmt, va_list args) { - HANDLE h; STRSAFE_LPWSTR pEnd = g_szFwprintfWbuf; int iBufSize; +#ifndef __CYGWIN__ + HANDLE h; DWORD cbWritten; +#endif // __CYGWIN__ g_szFwprintfWbuf[0] = L'\0'; g_szFwprintfBuf[0] = '\0'; @@ -135,11 +139,56 @@ PXCH_DLL_API void StdVwprintf(DWORD dwStdHandle, const WCHAR* fmt, va_list args) if (g_szFwprintfWbuf[PXCH_MAX_FWPRINTF_BUFSIZE - 2]) g_szFwprintfWbuf[PXCH_MAX_FWPRINTF_BUFSIZE - 2] = L'\n'; g_szFwprintfWbuf[PXCH_MAX_FWPRINTF_BUFSIZE - 1] = L'\0'; - iBufSize = WideCharToMultiByte(CP_ACP, 0, g_szFwprintfWbuf, (int)(pEnd - g_szFwprintfWbuf), g_szFwprintfBuf, PXCH_MAX_FWPRINTF_BUFSIZE, NULL, NULL); + iBufSize = WideCharToMultiByte( +#ifndef __CYGWIN__ + CP_ACP +#else // __CYGWIN__ + CP_UTF8 +#endif // __CYGWIN__ + , 0, g_szFwprintfWbuf, (int)(pEnd - g_szFwprintfWbuf), g_szFwprintfBuf, PXCH_MAX_FWPRINTF_BUFSIZE, NULL, NULL); g_szFwprintfBuf[PXCH_MAX_FWPRINTF_BUFSIZE - 1] = '\0'; +#ifndef __CYGWIN__ h = GetStdHandle(dwStdHandle); if (h && h != INVALID_HANDLE_VALUE) WriteFile(h, g_szFwprintfBuf, iBufSize, &cbWritten, NULL); +#else // __CYGWIN__ + DWORD dwWaitResult; + DWORD dwLastError; + + ODBGSTRLOGV(L"Waiting for g_hCygwinConsoleSemaphore."); + + dwWaitResult = WaitForSingleObject(g_hCygwinConsoleSemaphore, 0); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + if (dwStdHandle == STD_OUTPUT_HANDLE) { + pxch_cygwin_write(1, g_szFwprintfBuf, (size_t)iBufSize); + } else if (dwStdHandle == STD_ERROR_HANDLE) { + pxch_cygwin_write(2, g_szFwprintfBuf, (size_t)iBufSize); + } + if (!ReleaseSemaphore(g_hCygwinConsoleSemaphore, 1, NULL)) { + dwLastError = GetLastError(); + ODBGSTRLOGD(L"Release g_hCygwinConsoleSemaphore error: %ls", FormatErrorToStr(dwLastError)); + exit(dwLastError); + } + break; + + case WAIT_ABANDONED: + ODBGSTRLOGD(L"g_hCygwinConsoleSemaphore abandoned!"); + Sleep(INFINITE); + exit(ERROR_ABANDONED_WAIT_0); + break; + + case WAIT_TIMEOUT: + ODBGSTRLOGD(L"g_hCygwinConsoleSemaphore is currently unavailable, not outputing: %ls", g_szFwprintfWbuf); + break; + + default: + dwLastError = GetLastError(); + ODBGSTRLOGD(L"Wait for g_hCygwinConsoleSemaphore(%p) status: " WPRDW L"; error: %ls", g_hCygwinConsoleSemaphore, dwWaitResult, FormatErrorToStr(dwLastError)); + exit(dwLastError); + } +#endif // __CYGWIN__ } diff --git a/src/dll/hookdll_util_wsock.c b/src/dll/hookdll_util_wsock_win32.c similarity index 99% rename from src/dll/hookdll_util_wsock.c rename to src/dll/hookdll_util_wsock_win32.c index febee26..3251824 100644 --- a/src/dll/hookdll_util_wsock.c +++ b/src/dll/hookdll_util_wsock_win32.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* hookdll_util_wsock.c +/* hookdll_util_wsock_win32.c * Copyright (C) 2020 Feng Shun. * * This program is free software: you can redistribute it and/or modify diff --git a/src/exe/main.c b/src/exe/main.c index bb4ffcc..61f85a5 100644 --- a/src/exe/main.c +++ b/src/exe/main.c @@ -464,6 +464,7 @@ DWORD Init(void) { g_dwCurrentProcessIdForVerify = GetCurrentProcessId(); if ((g_hIpcServerSemaphore = CreateSemaphoreW(NULL, 0, 1, NULL)) == NULL) return GetLastError(); + if ((g_hCygwinConsoleSemaphore = CreateSemaphoreW(NULL, 1, 1, NULL)) == NULL) return GetLastError(); return 0; } @@ -502,7 +503,6 @@ int wmain(int argc, WCHAR* wargv[]) setvbuf(stderr, NULL, _IOFBF, 65536); szLocale = setlocale(LC_ALL, ""); - LOGD(L"Locale: " WPRS, szLocale); if ((dwError = Init()) != NOERROR) goto err; if ((dwError = InitProcessBookkeeping()) != NOERROR) goto err; @@ -584,6 +584,8 @@ void handle_sigchld(int sig) int iChildStatusTmp; BOOL bChildExitedNormally; int iChildExitStatus; + DWORD dwWaitResult; + DWORD dwLastError; while ((iChildPidTmp = waitpid((pid_t)(-1), &iChildStatusTmp, WNOHANG)) > 0) { bChild = TRUE; @@ -600,7 +602,28 @@ void handle_sigchld(int sig) } else { LOGI(L"Cygwin child process pid %d exited %ls(%d).", iChildPid, bChildExitedNormally ? L"normally" : L"ABNORMALLY", iChildExitStatus); } + // KillAllDescendant(); + + // Close cygwin console semaphore + dwWaitResult = WaitForSingleObject(g_hCygwinConsoleSemaphore, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + break; + + case WAIT_ABANDONED: + LOGC(L"g_hCygwinConsoleSemaphore abandoned!"); + Sleep(INFINITE); + exit(ERROR_ABANDONED_WAIT_0); + break; + + default: + dwLastError = GetLastError(); + LOGE(L"Wait for g_hCygwinConsoleSemaphore status: " WPRDW L"; error: %ls", dwWaitResult, FormatErrorToStr(dwLastError)); + exit(dwLastError); + } + exit(iChildExitStatus); } } @@ -655,15 +678,6 @@ int main(int argc, char* const argv[], char* const envp[]) setvbuf(stderr, NULL, _IOFBF, 65536); szLocale = setlocale(LC_ALL, ""); (void)szLocale; - // WriteFile() executed inside StdWprintf() in DLL won't work??? This is a workaround. - if (1) { - DWORD cbWritten; - WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "", 0, &cbWritten, NULL); - FlushFileBuffers(GetStdHandle(STD_OUTPUT_HANDLE)); - WriteFile(GetStdHandle(STD_ERROR_HANDLE), "", 0, &cbWritten, NULL); - FlushFileBuffers(GetStdHandle(STD_ERROR_HANDLE)); - } - LOGD(L"Locale: " WPRS, szLocale); if ((dwError = Init()) != NOERROR) goto err; if ((dwError = InitProcessBookkeeping()) != NOERROR) goto err; diff --git a/test/test.cpp b/test/test.cpp index f8a4b4a..ca6da9c 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -63,6 +63,8 @@ int main() { setlocale(LC_ALL, ""); + printf("WriteFile: %p\n", WriteFile); + char szIpStrNarrowBuf[100]; char szIpStrNarrowBuf2[100]; WCHAR szIpStrWBuf[100];