From 6da33b2af00f8df24909ca1563f50a2c1652fe16 Mon Sep 17 00:00:00 2001 From: Jason Hood Date: Sat, 8 Feb 2014 18:30:53 +1000 Subject: [PATCH] Attributes and saved position are local to each console window. --- ANSI.c | 272 ++++++++++++++++++++++++++++------------------------ ansicon.h | 16 +--- makefile.vc | 6 +- readme.txt | 7 +- 4 files changed, 156 insertions(+), 145 deletions(-) diff --git a/ANSI.c b/ANSI.c index 489ba2c..ebd59d7 100644 --- a/ANSI.c +++ b/ANSI.c @@ -118,7 +118,8 @@ ntdll's LdrLoadDll via CreateRemoteThread; restore original attributes on detach (for LoadLibrary/FreeLibrary usage); log: remove the quotes around the CreateProcess command line string and - distinguish NULL and "" args. + distinguish NULL and "" args; + attributes (and saved position) are local to each console window. */ #include "ansicon.h" @@ -127,15 +128,6 @@ #define is_digit(c) ('0' <= (c) && (c) <= '9') -#ifdef __GNUC__ -#define SHARED __attribute__((shared, section(".shared"))) -#else -#pragma data_seg(".shared", "read,write,shared") -#pragma data_seg() -#define SHARED __declspec(allocate(".shared")) -#endif - - // ========== Global variables and constants HANDLE hConOut; // handle to CONOUT$ @@ -246,36 +238,111 @@ const BYTE attr2ansi[8] = // map console attribute to ANSI number 7 // white }; -GRM grm; -// saved cursor position -COORD SavePos; - -// Variables to enable copying attributes between processes. -SHARED DWORD s_pid; -SHARED GRM s_grm; -SHARED DWORD s_flag; -#define GRM_INIT 1 -#define GRM_EXIT 2 - - -// Wait for the child process to finish, then update our GRM to the child's. -DWORD WINAPI UpdateGRM( LPVOID child_pi ) +typedef struct { - DWORD pid = ((LPPROCESS_INFORMATION)child_pi)->dwProcessId; - HANDLE proc = ((LPPROCESS_INFORMATION)child_pi)->hProcess; - free( child_pi ); + BYTE foreground; // ANSI base color (0 to 7; add 30) + BYTE background; // ANSI base color (0 to 7; add 40) + BYTE bold; // console FOREGROUND_INTENSITY bit + BYTE underline; // console BACKGROUND_INTENSITY bit + BYTE rvideo; // swap foreground/bold & background/underline + BYTE concealed; // set foreground/bold to background/underline + BYTE reverse; // swap console foreground & background attributes + COORD SavePos; // saved cursor position +} STATE, *PSTATE; - WaitForSingleObject( proc, INFINITE ); - CloseHandle( proc ); +PSTATE pState; +HANDLE hMap; - if (s_flag == GRM_EXIT && s_pid == pid) +void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO ); + + +void get_state( void ) +{ + TCHAR buf[64]; + HWND hwnd; + BOOL init; + HANDLE hConOut; + CONSOLE_SCREEN_BUFFER_INFO csbi; + static STATE state; // on the odd chance file mapping fails + + if (pState != NULL) + return; + + hwnd = GetConsoleWindow(); + if (hwnd == NULL) + return; + + wsprintf( buf, L"ANSICON_State_%X", PtrToUint( hwnd ) ); + hMap = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + 0, sizeof(STATE), buf ); + if (hMap == NULL) { - s_flag = 0; - grm = s_grm; + no_go: + DEBUGSTR( 1, L"File mapping failed (%lu) - using default state", + GetLastError() ); + pState = &state; + goto do_init; + } + init = (GetLastError() != ERROR_ALREADY_EXISTS); + + pState = MapViewOfFile( hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); + if (pState == NULL) + { + CloseHandle( hMap ); + goto no_go; } - return 0; + if (init) + { + do_init: + hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + if (!GetConsoleScreenBufferInfo( hConOut, &csbi )) + { + DEBUGSTR(1, L"Failed to get screen buffer info (%lu) - assuming defaults", + GetLastError() ); + csbi.wAttributes = 7; + csbi.dwSize.X = 80; + csbi.dwSize.Y = 300; + csbi.srWindow.Left = 0; + csbi.srWindow.Right = 79; + csbi.srWindow.Top = 0; + csbi.srWindow.Bottom = 24; + } + if (GetEnvironmentVariable( L"ANSICON_REVERSE", NULL, 0 )) + { + SetEnvironmentVariable( L"ANSICON_REVERSE", NULL ); + pState->reverse = TRUE; + pState->foreground = attr2ansi[(csbi.wAttributes >> 4) & 7]; + pState->background = attr2ansi[csbi.wAttributes & 7]; + pState->bold = (csbi.wAttributes & BACKGROUND_INTENSITY) >> 4; + pState->underline = (csbi.wAttributes & FOREGROUND_INTENSITY) << 4; + } + else + { + pState->foreground = attr2ansi[csbi.wAttributes & 7]; + pState->background = attr2ansi[(csbi.wAttributes >> 4) & 7]; + pState->bold = csbi.wAttributes & FOREGROUND_INTENSITY; + pState->underline = csbi.wAttributes & BACKGROUND_INTENSITY; + } + if (!GetEnvironmentVariable( L"ANSICON_DEF", NULL, 0 )) + { + TCHAR def[4]; + LPTSTR a = def; + if (pState->reverse) + { + *a++ = '-'; + csbi.wAttributes = ((csbi.wAttributes >> 4) & 15) + | ((csbi.wAttributes & 15) << 4); + } + wsprintf( a, L"%X", csbi.wAttributes & 255 ); + SetEnvironmentVariable( L"ANSICON_DEF", def ); + } + set_ansicon( &csbi ); + CloseHandle( hConOut ); + } } @@ -418,13 +485,14 @@ void InterpretEscSeq( void ) switch (suffix) { case 'm': + get_state(); if (es_argc == 0) es_argv[es_argc++] = 0; for (i = 0; i < es_argc; i++) { if (30 <= es_argv[i] && es_argv[i] <= 37) - grm.foreground = es_argv[i] - 30; + pState->foreground = es_argv[i] - 30; else if (40 <= es_argv[i] && es_argv[i] <= 47) - grm.background = es_argv[i] - 40; + pState->background = es_argv[i] - 40; else switch (es_argv[i]) { case 0: @@ -436,78 +504,78 @@ void InterpretEscSeq( void ) *def = '7'; def[1] = '\0'; GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) ); a = wcstol( def, NULL, 16 ); - grm.reverse = FALSE; + pState->reverse = FALSE; if (a < 0) { - grm.reverse = TRUE; + pState->reverse = TRUE; a = -a; } if (es_argv[i] != 49) - grm.foreground = attr2ansi[a & 7]; + pState->foreground = attr2ansi[a & 7]; if (es_argv[i] != 39) - grm.background = attr2ansi[(a >> 4) & 7]; + pState->background = attr2ansi[(a >> 4) & 7]; if (es_argv[i] == 0) { if (es_argc == 1) { - grm.bold = a & FOREGROUND_INTENSITY; - grm.underline = a & BACKGROUND_INTENSITY; + pState->bold = a & FOREGROUND_INTENSITY; + pState->underline = a & BACKGROUND_INTENSITY; } else { - grm.bold = 0; - grm.underline = 0; + pState->bold = 0; + pState->underline = 0; } - grm.rvideo = 0; - grm.concealed = 0; + pState->rvideo = 0; + pState->concealed = 0; } } break; - case 1: grm.bold = FOREGROUND_INTENSITY; break; + case 1: pState->bold = FOREGROUND_INTENSITY; break; case 5: // blink - case 4: grm.underline = BACKGROUND_INTENSITY; break; - case 7: grm.rvideo = 1; break; - case 8: grm.concealed = 1; break; + case 4: pState->underline = BACKGROUND_INTENSITY; break; + case 7: pState->rvideo = 1; break; + case 8: pState->concealed = 1; break; case 21: // oops, this actually turns on double underline // but xterm turns off bold too, so that's alright - case 22: grm.bold = 0; break; + case 22: pState->bold = 0; break; case 25: - case 24: grm.underline = 0; break; - case 27: grm.rvideo = 0; break; - case 28: grm.concealed = 0; break; + case 24: pState->underline = 0; break; + case 27: pState->rvideo = 0; break; + case 28: pState->concealed = 0; break; } } - if (grm.concealed) + if (pState->concealed) { - if (grm.rvideo) + if (pState->rvideo) { - attribut = foregroundcolor[grm.foreground] - | backgroundcolor[grm.foreground]; - if (grm.bold) + attribut = foregroundcolor[pState->foreground] + | backgroundcolor[pState->foreground]; + if (pState->bold) attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; } else { - attribut = foregroundcolor[grm.background] - | backgroundcolor[grm.background]; - if (grm.underline) + attribut = foregroundcolor[pState->background] + | backgroundcolor[pState->background]; + if (pState->underline) attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; } } - else if (grm.rvideo) + else if (pState->rvideo) { - attribut = foregroundcolor[grm.background] - | backgroundcolor[grm.foreground]; - if (grm.bold) + attribut = foregroundcolor[pState->background] + | backgroundcolor[pState->foreground]; + if (pState->bold) attribut |= BACKGROUND_INTENSITY; - if (grm.underline) + if (pState->underline) attribut |= FOREGROUND_INTENSITY; } else - attribut = foregroundcolor[grm.foreground] | grm.bold - | backgroundcolor[grm.background] | grm.underline; - if (grm.reverse) + attribut = foregroundcolor[pState->foreground] | pState->bold + | backgroundcolor[pState->background] | pState->underline; + if (pState->reverse) attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4); SetConsoleTextAttribute( hConOut, attribut ); return; @@ -762,12 +830,14 @@ void InterpretEscSeq( void ) case 's': // ESC[s Saves cursor position for recall later if (es_argc != 0) return; - SavePos = Info.dwCursorPosition; + get_state(); + pState->SavePos = Info.dwCursorPosition; return; case 'u': // ESC[u Return to saved cursor position if (es_argc != 0) return; - SetConsoleCursorPosition( hConOut, SavePos ); + get_state(); + SetConsoleCursorPosition( hConOut, pState->SavePos ); return; case 'n': // ESC[#n Device status report @@ -1309,19 +1379,6 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, #endif InjectDLL( child_pi, base ); #endif - if (!gui && !(dwCreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS))) - { - LPPROCESS_INFORMATION cpi; - s_pid = child_pi->dwProcessId; - s_grm = grm; - s_flag = GRM_INIT; - cpi = malloc( sizeof(*cpi) ); - cpi->dwProcessId = child_pi->dwProcessId; - DuplicateHandle( GetCurrentProcess(), child_pi->hProcess, - GetCurrentProcess(), &cpi->hProcess, 0, FALSE, - DUPLICATE_SAME_ACCESS ); - CloseHandle( CreateThread( NULL, 4096, UpdateGRM, cpi, 0, NULL ) ); - } } if (!(dwCreationFlags & CREATE_SUSPENDED)) @@ -1359,6 +1416,8 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName, (lpCommandLine == NULL) ? "" : (*lpCommandLine == '\0') ? "" : lpCommandLine ); + // May need to initialise the state, to propagate environment variables. + get_state(); if (!CreateProcessA( lpApplicationName, lpCommandLine, lpThreadAttributes, @@ -1401,6 +1460,7 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName, (lpCommandLine == NULL) ? L"" : (*lpCommandLine == '\0') ? L"" : lpCommandLine ); + get_state(); if (!CreateProcessW( lpApplicationName, lpCommandLine, lpThreadAttributes, @@ -1801,44 +1861,7 @@ void OriginalAttr( PVOID lpReserved ) break; } - if (s_flag == GRM_INIT && s_pid == GetCurrentProcessId()) - { - s_flag = 0; - grm = s_grm; - } - else - { - if (GetEnvironmentVariable( L"ANSICON_REVERSE", NULL, 0 )) - { - SetEnvironmentVariable( L"ANSICON_REVERSE", NULL ); - grm.reverse = TRUE; - grm.foreground = attr2ansi[(csbi.wAttributes >> 4) & 7]; - grm.background = attr2ansi[csbi.wAttributes & 7]; - grm.bold = (csbi.wAttributes & BACKGROUND_INTENSITY) >> 4; - grm.underline = (csbi.wAttributes & FOREGROUND_INTENSITY) << 4; - } - else - { - grm.foreground = attr2ansi[csbi.wAttributes & 7]; - grm.background = attr2ansi[(csbi.wAttributes >> 4) & 7]; - grm.bold = csbi.wAttributes & FOREGROUND_INTENSITY; - grm.underline = csbi.wAttributes & BACKGROUND_INTENSITY; - } - } - if (!GetEnvironmentVariable( L"ANSICON_DEF", NULL, 0 )) - { - TCHAR def[4]; - LPTSTR a = def; - if (grm.reverse) - { - *a++ = '-'; - csbi.wAttributes = ((csbi.wAttributes >> 4) & 15) - | ((csbi.wAttributes & 15) << 4); - } - wsprintf( a, L"%X", csbi.wAttributes & 255 ); - SetEnvironmentVariable( L"ANSICON_DEF", def ); - } - set_ansicon( &csbi ); + get_state(); } @@ -1896,9 +1919,6 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) else { DEBUGSTR( 1, L"Terminating" ); - s_pid = GetCurrentProcessId(); - s_grm = grm; - s_flag = GRM_EXIT; } if (orgattr != 0) { @@ -1908,6 +1928,8 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) SetConsoleTextAttribute( hConOut, orgattr ); CloseHandle( hConOut ); } + CloseHandle( pState ); + CloseHandle( hMap ); } return bResult; diff --git a/ansicon.h b/ansicon.h index fb0b272..97301fc 100644 --- a/ansicon.h +++ b/ansicon.h @@ -13,9 +13,9 @@ #define WIN32_LEAN_AND_MEAN #ifdef _WIN64 -#define _WIN32_WINNT 0x0600 // MinGW-w64 wants this defined for Wow64 stuff +#define _WIN32_WINNT 0x0501 // at least XP required #else -#define _WIN32_WINNT 0x0500 +#define _WIN32_WINNT 0x0500 // at least Windows 2000 required #endif #include #include @@ -49,18 +49,6 @@ #define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr ) -typedef struct -{ - BYTE foreground; // ANSI base color (0 to 7; add 30) - BYTE background; // ANSI base color (0 to 7; add 40) - BYTE bold; // console FOREGROUND_INTENSITY bit - BYTE underline; // console BACKGROUND_INTENSITY bit - BYTE rvideo; // swap foreground/bold & background/underline - BYTE concealed; // set foreground/bold to background/underline - BYTE reverse; // swap console foreground & background attributes -} GRM, *PGRM; // Graphic Rendition Mode - - int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* ); BOOL Wow64Process( HANDLE ); diff --git a/makefile.vc b/makefile.vc index 04febb9..81dd5fa 100644 --- a/makefile.vc +++ b/makefile.vc @@ -102,7 +102,7 @@ x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\procrva.obj x86\ansicon.res x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ - /base:0xAC0000 /section:.shared,s /filealign:512 + /base:0xAC0000 /filealign:512 !IF "$(_NMAKE_VER)" == "9.00.30729.01" $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 @del $@.manifest @@ -116,11 +116,11 @@ x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ - /base:0xAC000000 /section:.shared,s + /base:0xAC000000 x64\ANSI32.dll: x64\ANSI32.obj x64\proctype32.obj x86\injdll.obj x86\util.obj x86\ansi.res $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ - /base:0xAC0000 /section:.shared,s /filealign:512 /largeaddressaware + /base:0xAC0000 /filealign:512 /largeaddressaware !IF "$(_NMAKE_VER)" == "9.00.30729.01" $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 @del $@.manifest diff --git a/readme.txt b/readme.txt index cc96383..c3dd787 100644 --- a/readme.txt +++ b/readme.txt @@ -273,14 +273,15 @@ Version History Legend: + added, - bug-fixed, * changed. - 1.70 - 4 February, 2014: + 1.70 - 8 February, 2014: - don't hook again if using LoadLibrary or LoadLibraryEx; - update the LoadLibraryEx flags that shouldn't hook; - restore original attributes on detach (for LoadLibrary/FreeLibrary usage); - ansicon.exe will start with ANSICON_DEF (if defined and -m not used); - an installed ansicon.exe will restore current (not default) attributes; + - attributes and saved position are local to each console window; * inject into a created process by modifying the import descriptor table - (use CreateRemoteThread for -p); + (-p will use CreateRemoteThread); * log: remove the quotes around the CreateProcess command line; add an underscore in 64-bit addresses to distinguish 8-digit groups. @@ -470,4 +471,4 @@ Distribution ============================= -Jason Hood, 4 February, 2014. +Jason Hood, 8 February, 2014.