Attributes and saved position are local to each console window.

This commit is contained in:
Jason Hood 2014-02-08 18:30:53 +10:00
parent 453db81af5
commit 6da33b2af0
4 changed files with 156 additions and 145 deletions

272
ANSI.c
View File

@ -118,7 +118,8 @@
ntdll's LdrLoadDll via CreateRemoteThread; ntdll's LdrLoadDll via CreateRemoteThread;
restore original attributes on detach (for LoadLibrary/FreeLibrary usage); restore original attributes on detach (for LoadLibrary/FreeLibrary usage);
log: remove the quotes around the CreateProcess command line string and 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" #include "ansicon.h"
@ -127,15 +128,6 @@
#define is_digit(c) ('0' <= (c) && (c) <= '9') #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 // ========== Global variables and constants
HANDLE hConOut; // handle to CONOUT$ HANDLE hConOut; // handle to CONOUT$
@ -246,36 +238,111 @@ const BYTE attr2ansi[8] = // map console attribute to ANSI number
7 // white 7 // white
}; };
GRM grm;
// saved cursor position typedef struct
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 )
{ {
DWORD pid = ((LPPROCESS_INFORMATION)child_pi)->dwProcessId; BYTE foreground; // ANSI base color (0 to 7; add 30)
HANDLE proc = ((LPPROCESS_INFORMATION)child_pi)->hProcess; BYTE background; // ANSI base color (0 to 7; add 40)
free( child_pi ); 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 ); PSTATE pState;
CloseHandle( proc ); 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; no_go:
grm = s_grm; 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) switch (suffix)
{ {
case 'm': case 'm':
get_state();
if (es_argc == 0) es_argv[es_argc++] = 0; if (es_argc == 0) es_argv[es_argc++] = 0;
for (i = 0; i < es_argc; i++) for (i = 0; i < es_argc; i++)
{ {
if (30 <= es_argv[i] && es_argv[i] <= 37) 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) 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]) else switch (es_argv[i])
{ {
case 0: case 0:
@ -436,78 +504,78 @@ void InterpretEscSeq( void )
*def = '7'; def[1] = '\0'; *def = '7'; def[1] = '\0';
GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) ); GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) );
a = wcstol( def, NULL, 16 ); a = wcstol( def, NULL, 16 );
grm.reverse = FALSE; pState->reverse = FALSE;
if (a < 0) if (a < 0)
{ {
grm.reverse = TRUE; pState->reverse = TRUE;
a = -a; a = -a;
} }
if (es_argv[i] != 49) if (es_argv[i] != 49)
grm.foreground = attr2ansi[a & 7]; pState->foreground = attr2ansi[a & 7];
if (es_argv[i] != 39) if (es_argv[i] != 39)
grm.background = attr2ansi[(a >> 4) & 7]; pState->background = attr2ansi[(a >> 4) & 7];
if (es_argv[i] == 0) if (es_argv[i] == 0)
{ {
if (es_argc == 1) if (es_argc == 1)
{ {
grm.bold = a & FOREGROUND_INTENSITY; pState->bold = a & FOREGROUND_INTENSITY;
grm.underline = a & BACKGROUND_INTENSITY; pState->underline = a & BACKGROUND_INTENSITY;
} }
else else
{ {
grm.bold = 0; pState->bold = 0;
grm.underline = 0; pState->underline = 0;
} }
grm.rvideo = 0; pState->rvideo = 0;
grm.concealed = 0; pState->concealed = 0;
} }
} }
break; break;
case 1: grm.bold = FOREGROUND_INTENSITY; break; case 1: pState->bold = FOREGROUND_INTENSITY; break;
case 5: // blink case 5: // blink
case 4: grm.underline = BACKGROUND_INTENSITY; break; case 4: pState->underline = BACKGROUND_INTENSITY; break;
case 7: grm.rvideo = 1; break; case 7: pState->rvideo = 1; break;
case 8: grm.concealed = 1; break; case 8: pState->concealed = 1; break;
case 21: // oops, this actually turns on double underline case 21: // oops, this actually turns on double underline
// but xterm turns off bold too, so that's alright // 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 25:
case 24: grm.underline = 0; break; case 24: pState->underline = 0; break;
case 27: grm.rvideo = 0; break; case 27: pState->rvideo = 0; break;
case 28: grm.concealed = 0; break; case 28: pState->concealed = 0; break;
} }
} }
if (grm.concealed) if (pState->concealed)
{ {
if (grm.rvideo) if (pState->rvideo)
{ {
attribut = foregroundcolor[grm.foreground] attribut = foregroundcolor[pState->foreground]
| backgroundcolor[grm.foreground]; | backgroundcolor[pState->foreground];
if (grm.bold) if (pState->bold)
attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
} }
else else
{ {
attribut = foregroundcolor[grm.background] attribut = foregroundcolor[pState->background]
| backgroundcolor[grm.background]; | backgroundcolor[pState->background];
if (grm.underline) if (pState->underline)
attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
} }
} }
else if (grm.rvideo) else if (pState->rvideo)
{ {
attribut = foregroundcolor[grm.background] attribut = foregroundcolor[pState->background]
| backgroundcolor[grm.foreground]; | backgroundcolor[pState->foreground];
if (grm.bold) if (pState->bold)
attribut |= BACKGROUND_INTENSITY; attribut |= BACKGROUND_INTENSITY;
if (grm.underline) if (pState->underline)
attribut |= FOREGROUND_INTENSITY; attribut |= FOREGROUND_INTENSITY;
} }
else else
attribut = foregroundcolor[grm.foreground] | grm.bold attribut = foregroundcolor[pState->foreground] | pState->bold
| backgroundcolor[grm.background] | grm.underline; | backgroundcolor[pState->background] | pState->underline;
if (grm.reverse) if (pState->reverse)
attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4); attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4);
SetConsoleTextAttribute( hConOut, attribut ); SetConsoleTextAttribute( hConOut, attribut );
return; return;
@ -762,12 +830,14 @@ void InterpretEscSeq( void )
case 's': // ESC[s Saves cursor position for recall later case 's': // ESC[s Saves cursor position for recall later
if (es_argc != 0) return; if (es_argc != 0) return;
SavePos = Info.dwCursorPosition; get_state();
pState->SavePos = Info.dwCursorPosition;
return; return;
case 'u': // ESC[u Return to saved cursor position case 'u': // ESC[u Return to saved cursor position
if (es_argc != 0) return; if (es_argc != 0) return;
SetConsoleCursorPosition( hConOut, SavePos ); get_state();
SetConsoleCursorPosition( hConOut, pState->SavePos );
return; return;
case 'n': // ESC[#n Device status report case 'n': // ESC[#n Device status report
@ -1309,19 +1379,6 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
#endif #endif
InjectDLL( child_pi, base ); InjectDLL( child_pi, base );
#endif #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)) if (!(dwCreationFlags & CREATE_SUSPENDED))
@ -1359,6 +1416,8 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName,
(lpCommandLine == NULL) ? "<null>" : (lpCommandLine == NULL) ? "<null>" :
(*lpCommandLine == '\0') ? "<empty>" : lpCommandLine ); (*lpCommandLine == '\0') ? "<empty>" : lpCommandLine );
// May need to initialise the state, to propagate environment variables.
get_state();
if (!CreateProcessA( lpApplicationName, if (!CreateProcessA( lpApplicationName,
lpCommandLine, lpCommandLine,
lpThreadAttributes, lpThreadAttributes,
@ -1401,6 +1460,7 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName,
(lpCommandLine == NULL) ? L"<null>" : (lpCommandLine == NULL) ? L"<null>" :
(*lpCommandLine == '\0') ? L"<empty>" : lpCommandLine ); (*lpCommandLine == '\0') ? L"<empty>" : lpCommandLine );
get_state();
if (!CreateProcessW( lpApplicationName, if (!CreateProcessW( lpApplicationName,
lpCommandLine, lpCommandLine,
lpThreadAttributes, lpThreadAttributes,
@ -1801,44 +1861,7 @@ void OriginalAttr( PVOID lpReserved )
break; break;
} }
if (s_flag == GRM_INIT && s_pid == GetCurrentProcessId()) get_state();
{
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 );
} }
@ -1896,9 +1919,6 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
else else
{ {
DEBUGSTR( 1, L"Terminating" ); DEBUGSTR( 1, L"Terminating" );
s_pid = GetCurrentProcessId();
s_grm = grm;
s_flag = GRM_EXIT;
} }
if (orgattr != 0) if (orgattr != 0)
{ {
@ -1908,6 +1928,8 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
SetConsoleTextAttribute( hConOut, orgattr ); SetConsoleTextAttribute( hConOut, orgattr );
CloseHandle( hConOut ); CloseHandle( hConOut );
} }
CloseHandle( pState );
CloseHandle( hMap );
} }
return bResult; return bResult;

View File

@ -13,9 +13,9 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#ifdef _WIN64 #ifdef _WIN64
#define _WIN32_WINNT 0x0600 // MinGW-w64 wants this defined for Wow64 stuff #define _WIN32_WINNT 0x0501 // at least XP required
#else #else
#define _WIN32_WINNT 0x0500 #define _WIN32_WINNT 0x0500 // at least Windows 2000 required
#endif #endif
#include <windows.h> #include <windows.h>
#include <stdio.h> #include <stdio.h>
@ -49,18 +49,6 @@
#define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr ) #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* ); int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* );
BOOL Wow64Process( HANDLE ); BOOL Wow64Process( HANDLE );

View File

@ -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 x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ $(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" !IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
@del $@.manifest @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 x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ $(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 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 \ $(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" !IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
@del $@.manifest @del $@.manifest

View File

@ -273,14 +273,15 @@ Version History
Legend: + added, - bug-fixed, * changed. Legend: + added, - bug-fixed, * changed.
1.70 - 4 February, 2014: 1.70 - 8 February, 2014:
- don't hook again if using LoadLibrary or LoadLibraryEx; - don't hook again if using LoadLibrary or LoadLibraryEx;
- update the LoadLibraryEx flags that shouldn't hook; - update the LoadLibraryEx flags that shouldn't hook;
- restore original attributes on detach (for LoadLibrary/FreeLibrary usage); - restore original attributes on detach (for LoadLibrary/FreeLibrary usage);
- ansicon.exe will start with ANSICON_DEF (if defined and -m not used); - ansicon.exe will start with ANSICON_DEF (if defined and -m not used);
- an installed ansicon.exe will restore current (not default) attributes; - 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 * 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; * log: remove the quotes around the CreateProcess command line;
add an underscore in 64-bit addresses to distinguish 8-digit groups. 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.