Skip to content

Commit

Permalink
fix: log: switch from WriteFile(StdHandle) to write(1/2) under Cygwin…
Browse files Browse the repository at this point in the history
…, resolving Cygwin UTF-8 output encoding issue
  • Loading branch information
shunf4 committed Jun 23, 2020
1 parent ceeacfa commit 816e8e6
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 30 deletions.
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions cygwin_build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
4 changes: 4 additions & 0 deletions cygwin_build/test/fork_exec/test5.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
5 changes: 3 additions & 2 deletions include/hookdll_util_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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);
6 changes: 3 additions & 3 deletions proxychains_hook.dll/proxychains_hook.dll.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@
<ClCompile Include="..\src\dll\hook_connect_win32.c" />
<ClCompile Include="..\src\dll\hook_installer.c" />
<ClCompile Include="..\src\dll\ipc_client_and_child_data.c" />
<ClCompile Include="..\src\dll\hookdll_util.c" />
<ClCompile Include="..\src\dll\hookdll_util_wsock.c" />
<ClCompile Include="..\src\dll\hookdll_util_ipc_message.c" />
<ClCompile Include="..\src\dll\hookdll_util_log_win32.c" />
<ClCompile Include="..\src\dll\hookdll_util_wsock_win32.c" />
<ClCompile Include="..\src\dll\hookdll_util_ipc_message_win32.c" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\minhook\build\VC15\libMinHook.vcxproj">
Expand Down
6 changes: 3 additions & 3 deletions proxychains_hook.dll/proxychains_hook.dll.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
<ClCompile Include="..\src\dll\hook_installer.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\src\dll\hookdll_util.c">
<ClCompile Include="..\src\dll\hookdll_util_log_win32.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\src\dll\hookdll_util_wsock.c">
<ClCompile Include="..\src\dll\hookdll_util_wsock_win32.c">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="..\src\dll\hookdll_util_ipc_message.c">
<ClCompile Include="..\src\dll\hookdll_util_ipc_message_win32.c">
<Filter>Sources</Filter>
</ClCompile>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
31 changes: 31 additions & 0 deletions src/dll/hookdll_util_log_cygwin.c
Original file line number Diff line number Diff line change
@@ -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
* <http://www.gnu.org/licenses/>.
*/
#include "includes_generic.h"
#include "defines_generic.h"
#include <unistd.h>

#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);
}
55 changes: 52 additions & 3 deletions src/dll/hookdll_util.c → src/dll/hookdll_util_log_win32.c
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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);

Expand Down Expand Up @@ -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';
Expand All @@ -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__
}


Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
34 changes: 24 additions & 10 deletions src/exe/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ int main()
{
setlocale(LC_ALL, "");

printf("WriteFile: %p\n", WriteFile);

char szIpStrNarrowBuf[100];
char szIpStrNarrowBuf2[100];
WCHAR szIpStrWBuf[100];
Expand Down

0 comments on commit 816e8e6

Please sign in to comment.