From: shingil.kang Date: Tue, 3 Jun 2014 02:17:56 +0000 (+0900) Subject: SDB: Upgraded ansicon version 1.66 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ea69a1385b59b90370d07191b6ea6c3066004ef1;p=sdk%2Ftools%2Fsdb.git SDB: Upgraded ansicon version 1.66 Change-Id: I29e7ebda52c434e5949b0b76ab3b2eeef60442c0 Signed-off-by: shingil.kang --- diff --git a/ansicon/ANSI-LLW.c b/ansicon/ANSI-LLW.c deleted file mode 100755 index 28991f0..0000000 --- a/ansicon/ANSI-LLW.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - ANSI-LLW.c - Output the 32-bit address of LoadLibraryW. - - Jason Hood, 13 November, 2010 (LLA version 5 September, 2010). - - I don't know of a method to retrieve the 32-bit address of a function in - 64-bit code, so this is a simple workaround. - - 18 December, 2010: Initially I used GetProcAddress, but then I thought that - was silly, why don't I just return LoadLibraryW directly? That worked fine - for TDM64 and VC, but MinGW32 would return the address of the jump to the - function, not the function itself. Not so silly after all. -*/ - -#define WIN32_LEAN_AND_MEAN -#include - -int main( void ) -{ - return (DWORD)GetProcAddress( GetModuleHandle( "kernel32.dll" ), - "LoadLibraryW" ); -} diff --git a/ansicon/ANSI.c b/ansicon/ANSI.c old mode 100755 new mode 100644 index 3907252..c5b08a7 --- a/ansicon/ANSI.c +++ b/ansicon/ANSI.c @@ -1,1825 +1,1860 @@ -/* - ANSI.c - ANSI escape sequence console driver. - - Jason Hood, 21 & 22 October, 2005. - - Derived from ANSI.xs by Jean-Louis Morel, from his Perl package - Win32::Console::ANSI. I removed the codepage conversion ("\e(") and added - WriteConsole hooking. - - v1.01, 11 & 12 March, 2006: - disable when console has disabled processed output; - \e[5m (blink) is the same as \e[4m (underline); - do not conceal control characters (0 to 31); - \e[m will restore original color. - - v1.10, 22 February, 2009: - fix MyWriteConsoleW for strings longer than the buffer; - initialise attributes to current; - hook into child processes. - - v1.11, 28 February, 2009: - fix hooking into child processes (only do console executables). - - v1.12, 9 March, 2009: - really fix hooking (I didn't realise MinGW didn't generate relocations). - - v1.13, 21 & 27 March, 2009: - alternate injection method, to work with DEP; - use Unicode and the current output code page (not OEMCP). - - v1.14, 3 April, 2009: - fix test for empty import section. - - v1.15, 17 May, 2009: - properly update lpNumberOfCharsWritten in MyWriteConsoleA. - - v1.20, 26 & 29 May, 17 to 21 June, 2009: - create an ANSICON environment variable; - hook GetEnvironmentVariable to create ANSICON dynamically; - use another injection method. - - v1.22, 5 October, 2009: - hook LoadLibrary to intercept the newly loaded functions. - - v1.23, 11 November, 2009: - unload gracefully; - conceal characters by making foreground same as background; - reverse the bold/underline attributes, too. - - v1.25, 15, 20 & 21 July, 2010: - hook LoadLibraryEx (now cscript works); - Win7 support. - - v1.30, 3 August to 7 September, 2010: - x64 support. - - v1.31, 13 & 19 November, 2010: - fix multibyte conversion problems. - - v1.32, 4 to 22 December, 2010: - test for lpNumberOfCharsWritten/lpNumberOfBytesWritten being NULL; - recognise DSR and xterm window title; - ignore sequences starting with \e[? & \e[>; - close the handles opened by CreateProcess. - - v1.40, 25 & 26 February, 1 March, 2011: - hook GetProcAddress, addresses issues with .NET (work with PowerShell); - implement SO & SI to use the DEC Special Graphics Character Set (enables - line drawing via ASCII); ignore \e(X & \e)X (where X is any character); - add \e[?25h & \e[?25l to show/hide the cursor (DECTCEM). - - v1.50, 7 to 14 December, 2011: - added dynamic environment variable ANSICON_VER to return version; - read ANSICON_EXC environment variable to exclude selected modules; - read ANSICON_GUI environment variable to hook selected GUI programs; - read ANSICON_DEF environment variable to set the default GR; - transfer current GR to child, read it on exit. - - v1.51, 15 January, 5, 22 & 24 February, 2012: - added log mask 16 to log all the imported modules of imported modules; - ignore the version within the core API DLL names; - fix 32-bit process trying to identify 64-bit process; - hook _lwrite & _hwrite. - - v1.52, 10 April, 1 & 2 June, 2012: - use ansicon.exe to enable 32-bit to inject into 64-bit; - implement \e[39m & \e[49m (only setting color, nothing else); - added the character/line equivalents (keaj`) of the cursor movement - sequences (ABCDG), as well as vertical absolute (d) and erase characters - (X). - - v1.53, 12 June, 2012: - fixed Update_GRM when running multiple processes (e.g. "cl /MP"). -*/ - -#include "ansicon.h" -#include "version.h" -#include - -#define isdigit(c) ('0' <= (c) && (c) <= '9') - -#ifdef __GNUC__ -#define SHARED __attribute__((shared, section(".share"))) -#else -#pragma section(".shared", read,write,shared) -#define SHARED __declspec(allocate(".shared")) -#endif - - -// ========== Global variables and constants - -HANDLE hConOut; // handle to CONOUT$ - -#define ESC '\x1B' // ESCape character -#define BEL '\x07' -#define SO '\x0E' // Shift Out -#define SI '\x0F' // Shift In - -#define MAX_ARG 16 // max number of args in an escape sequence -int state; // automata state -TCHAR prefix; // escape sequence prefix ( '[', ']' or '(' ); -TCHAR prefix2; // secondary prefix ( '?' or '>' ); -TCHAR suffix; // escape sequence suffix -int es_argc; // escape sequence args count -int es_argv[MAX_ARG]; // escape sequence args -TCHAR Pt_arg[MAX_PATH*2]; // text parameter for Operating System Command -int Pt_len; -BOOL shifted; - - -// DEC Special Graphics Character Set from -// http://vt100.net/docs/vt220-rm/table2-4.html -// Some of these may not look right, depending on the font and code page (in -// particular, the Control Pictures probably won't work at all). -const WCHAR G1[] = -{ - ' ', // _ - blank - L'\x2666', // ` - Black Diamond Suit - L'\x2592', // a - Medium Shade - L'\x2409', // b - HT - L'\x240c', // c - FF - L'\x240d', // d - CR - L'\x240a', // e - LF - L'\x00b0', // f - Degree Sign - L'\x00b1', // g - Plus-Minus Sign - L'\x2424', // h - NL - L'\x240b', // i - VT - L'\x2518', // j - Box Drawings Light Up And Left - L'\x2510', // k - Box Drawings Light Down And Left - L'\x250c', // l - Box Drawings Light Down And Right - L'\x2514', // m - Box Drawings Light Up And Right - L'\x253c', // n - Box Drawings Light Vertical And Horizontal - L'\x00af', // o - SCAN 1 - Macron - L'\x25ac', // p - SCAN 3 - Black Rectangle - L'\x2500', // q - SCAN 5 - Box Drawings Light Horizontal - L'_', // r - SCAN 7 - Low Line - L'_', // s - SCAN 9 - Low Line - L'\x251c', // t - Box Drawings Light Vertical And Right - L'\x2524', // u - Box Drawings Light Vertical And Left - L'\x2534', // v - Box Drawings Light Up And Horizontal - L'\x252c', // w - Box Drawings Light Down And Horizontal - L'\x2502', // x - Box Drawings Light Vertical - L'\x2264', // y - Less-Than Or Equal To - L'\x2265', // z - Greater-Than Or Equal To - L'\x03c0', // { - Greek Small Letter Pi - L'\x2260', // | - Not Equal To - L'\x00a3', // } - Pound Sign - L'\x00b7', // ~ - Middle Dot -}; - -#define FIRST_G1 '_' -#define LAST_G1 '~' - - -// color constants - -#define FOREGROUND_BLACK 0 -#define FOREGROUND_WHITE FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE - -#define BACKGROUND_BLACK 0 -#define BACKGROUND_WHITE BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE - -const BYTE foregroundcolor[8] = -{ - FOREGROUND_BLACK, // black foreground - FOREGROUND_RED, // red foreground - FOREGROUND_GREEN, // green foreground - FOREGROUND_RED | FOREGROUND_GREEN, // yellow foreground - FOREGROUND_BLUE, // blue foreground - FOREGROUND_BLUE | FOREGROUND_RED, // magenta foreground - FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan foreground - FOREGROUND_WHITE // white foreground -}; - -const BYTE backgroundcolor[8] = -{ - BACKGROUND_BLACK, // black background - BACKGROUND_RED, // red background - BACKGROUND_GREEN, // green background - BACKGROUND_RED | BACKGROUND_GREEN, // yellow background - BACKGROUND_BLUE, // blue background - BACKGROUND_BLUE | BACKGROUND_RED, // magenta background - BACKGROUND_BLUE | BACKGROUND_GREEN, // cyan background - BACKGROUND_WHITE, // white background -}; - -const BYTE attr2ansi[8] = // map console attribute to ANSI number -{ - 0, // black - 4, // blue - 2, // green - 6, // cyan - 1, // red - 5, // magenta - 3, // yellow - 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 ) -{ - DWORD pid = ((LPPROCESS_INFORMATION)child_pi)->dwProcessId; - HANDLE proc = ((LPPROCESS_INFORMATION)child_pi)->hProcess; - free( child_pi ); - - WaitForSingleObject( proc, INFINITE ); - CloseHandle( proc ); - - if (s_flag == GRM_EXIT && s_pid == pid) - { - s_flag = 0; - grm = s_grm; - } - - return 0; -} - - -// Search an environment variable for a string. -BOOL search_env( LPCTSTR var, LPCTSTR val ) -{ - static LPTSTR env; - static DWORD env_len; - DWORD len; - BOOL not; - - len = GetEnvironmentVariable( var, env, env_len ); - if (len == 0) - return FALSE; - - if (len > env_len) - { - LPTSTR tmp = realloc( env, TSIZE(len) ); - if (tmp == NULL) - return FALSE; - env = tmp; - env_len = len; - GetEnvironmentVariable( var, env, env_len ); - } - - not = (*env == '!'); - if (not && env[1] == '\0') - return TRUE; - - for (var = wcstok( env + not, L";" ); var; var = wcstok( NULL, L";" )) - if (_wcsicmp( val, var ) == 0) - return !not; - - return not; -} - - -// ========== Print Buffer functions - -#define BUFFER_SIZE 2048 - -int nCharInBuffer; -WCHAR ChBuffer[BUFFER_SIZE]; - -//----------------------------------------------------------------------------- -// FlushBuffer() -// Writes the buffer to the console and empties it. -//----------------------------------------------------------------------------- - -void FlushBuffer( void ) -{ - DWORD nWritten; - if (nCharInBuffer <= 0) return; - WriteConsole( hConOut, ChBuffer, nCharInBuffer, &nWritten, NULL ); - nCharInBuffer = 0; -} - -//----------------------------------------------------------------------------- -// PushBuffer( WCHAR c ) -// Adds a character in the buffer. -//----------------------------------------------------------------------------- - -void PushBuffer( WCHAR c ) -{ - if (shifted && c >= FIRST_G1 && c <= LAST_G1) - c = G1[c-FIRST_G1]; - ChBuffer[nCharInBuffer] = c; - if (++nCharInBuffer == BUFFER_SIZE) - FlushBuffer(); -} - -//----------------------------------------------------------------------------- -// SendSequence( LPTSTR seq ) -// Send the string to the input buffer. -//----------------------------------------------------------------------------- - -void SendSequence( LPTSTR seq ) -{ - DWORD out; - INPUT_RECORD in; - HANDLE hStdIn = GetStdHandle( STD_INPUT_HANDLE ); - - in.EventType = KEY_EVENT; - in.Event.KeyEvent.bKeyDown = TRUE; - in.Event.KeyEvent.wRepeatCount = 1; - in.Event.KeyEvent.wVirtualKeyCode = 0; - in.Event.KeyEvent.wVirtualScanCode = 0; - in.Event.KeyEvent.dwControlKeyState = 0; - for (; *seq; ++seq) - { - in.Event.KeyEvent.uChar.UnicodeChar = *seq; - WriteConsoleInput( hStdIn, &in, 1, &out ); - } -} - -// ========== Print functions - -//----------------------------------------------------------------------------- -// InterpretEscSeq() -// Interprets the last escape sequence scanned by ParseAndPrintString -// prefix escape sequence prefix -// es_argc escape sequence args count -// es_argv[] escape sequence args array -// suffix escape sequence suffix -// -// for instance, with \e[33;45;1m we have -// prefix = '[', -// es_argc = 3, es_argv[0] = 33, es_argv[1] = 45, es_argv[2] = 1 -// suffix = 'm' -//----------------------------------------------------------------------------- - -void InterpretEscSeq( void ) -{ - int i; - WORD attribut; - CONSOLE_SCREEN_BUFFER_INFO Info; - CONSOLE_CURSOR_INFO CursInfo; - DWORD len, NumberOfCharsWritten; - COORD Pos; - SMALL_RECT Rect; - CHAR_INFO CharInfo; - - if (prefix == '[') - { - if (prefix2 == '?' && (suffix == 'h' || suffix == 'l')) - { - if (es_argc == 1 && es_argv[0] == 25) - { - GetConsoleCursorInfo( hConOut, &CursInfo ); - CursInfo.bVisible = (suffix == 'h'); - SetConsoleCursorInfo( hConOut, &CursInfo ); - return; - } - } - // Ignore any other \e[? or \e[> sequences. - if (prefix2 != 0) - return; - - GetConsoleScreenBufferInfo( hConOut, &Info ); - switch (suffix) - { - case 'm': - 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; - else if (40 <= es_argv[i] && es_argv[i] <= 47) - grm.background = es_argv[i] - 40; - else switch (es_argv[i]) - { - case 0: - case 39: - case 49: - { - TCHAR def[4]; - int a; - *def = '7'; def[1] = '\0'; - GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) ); - a = wcstol( def, NULL, 16 ); - grm.reverse = FALSE; - if (a < 0) - { - grm.reverse = TRUE; - a = -a; - } - if (es_argv[i] != 49) - grm.foreground = attr2ansi[a & 7]; - if (es_argv[i] != 39) - grm.background = attr2ansi[(a >> 4) & 7]; - if (es_argv[i] == 0) - { - if (es_argc == 1) - { - grm.bold = a & FOREGROUND_INTENSITY; - grm.underline = a & BACKGROUND_INTENSITY; - } - else - { - grm.bold = 0; - grm.underline = 0; - } - grm.rvideo = 0; - grm.concealed = 0; - } - } - break; - - case 1: grm.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 21: // oops, this actually turns on double underline - case 22: grm.bold = 0; break; - case 25: - case 24: grm.underline = 0; break; - case 27: grm.rvideo = 0; break; - case 28: grm.concealed = 0; break; - } - } - if (grm.concealed) - { - if (grm.rvideo) - { - attribut = foregroundcolor[grm.foreground] - | backgroundcolor[grm.foreground]; - if (grm.bold) - attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; - } - else - { - attribut = foregroundcolor[grm.background] - | backgroundcolor[grm.background]; - if (grm.underline) - attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; - } - } - else if (grm.rvideo) - { - attribut = foregroundcolor[grm.background] - | backgroundcolor[grm.foreground]; - if (grm.bold) - attribut |= BACKGROUND_INTENSITY; - if (grm.underline) - attribut |= FOREGROUND_INTENSITY; - } - else - attribut = foregroundcolor[grm.foreground] | grm.bold - | backgroundcolor[grm.background] | grm.underline; - if (grm.reverse) - attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4); - SetConsoleTextAttribute( hConOut, attribut ); - return; - - case 'J': - if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[J == ESC[0J - if (es_argc != 1) return; - switch (es_argv[0]) - { - case 0: // ESC[0J erase from cursor to end of display - len = (Info.dwSize.Y - Info.dwCursorPosition.Y - 1) * Info.dwSize.X - + Info.dwSize.X - Info.dwCursorPosition.X - 1; - FillConsoleOutputCharacter( hConOut, ' ', len, - Info.dwCursorPosition, - &NumberOfCharsWritten ); - FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, - Info.dwCursorPosition, - &NumberOfCharsWritten ); - return; - - case 1: // ESC[1J erase from start to cursor. - Pos.X = 0; - Pos.Y = 0; - len = Info.dwCursorPosition.Y * Info.dwSize.X - + Info.dwCursorPosition.X + 1; - FillConsoleOutputCharacter( hConOut, ' ', len, Pos, - &NumberOfCharsWritten ); - FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, Pos, - &NumberOfCharsWritten ); - return; - - case 2: // ESC[2J Clear screen and home cursor - Pos.X = 0; - Pos.Y = 0; - len = Info.dwSize.X * Info.dwSize.Y; - FillConsoleOutputCharacter( hConOut, ' ', len, Pos, - &NumberOfCharsWritten ); - FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, Pos, - &NumberOfCharsWritten ); - SetConsoleCursorPosition( hConOut, Pos ); - return; - - default: - return; - } - - case 'K': - if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[K == ESC[0K - if (es_argc != 1) return; - switch (es_argv[0]) - { - case 0: // ESC[0K Clear to end of line - len = Info.srWindow.Right - Info.dwCursorPosition.X + 1; - FillConsoleOutputCharacter( hConOut, ' ', len, - Info.dwCursorPosition, - &NumberOfCharsWritten ); - FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, - Info.dwCursorPosition, - &NumberOfCharsWritten ); - return; - - case 1: // ESC[1K Clear from start of line to cursor - Pos.X = 0; - Pos.Y = Info.dwCursorPosition.Y; - FillConsoleOutputCharacter( hConOut, ' ', - Info.dwCursorPosition.X + 1, Pos, - &NumberOfCharsWritten ); - FillConsoleOutputAttribute( hConOut, Info.wAttributes, - Info.dwCursorPosition.X + 1, Pos, - &NumberOfCharsWritten ); - return; - - case 2: // ESC[2K Clear whole line. - Pos.X = 0; - Pos.Y = Info.dwCursorPosition.Y; - FillConsoleOutputCharacter( hConOut, ' ', Info.dwSize.X, Pos, - &NumberOfCharsWritten ); - FillConsoleOutputAttribute( hConOut, Info.wAttributes, - Info.dwSize.X, Pos, - &NumberOfCharsWritten ); - return; - - default: - return; - } - - case 'X': // ESC[#X Erase # characters. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[X == ESC[1X - if (es_argc != 1) return; - FillConsoleOutputCharacter( hConOut, ' ', es_argv[0], - Info.dwCursorPosition, - &NumberOfCharsWritten ); - FillConsoleOutputAttribute( hConOut, Info.wAttributes, es_argv[0], - Info.dwCursorPosition, - &NumberOfCharsWritten ); - return; - - case 'L': // ESC[#L Insert # blank lines. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[L == ESC[1L - if (es_argc != 1) return; - Rect.Left = 0; - Rect.Top = Info.dwCursorPosition.Y; - Rect.Right = Info.dwSize.X - 1; - Rect.Bottom = Info.dwSize.Y - 1; - Pos.X = 0; - Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; - CharInfo.Char.UnicodeChar = ' '; - CharInfo.Attributes = Info.wAttributes; - ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo ); - return; - - case 'M': // ESC[#M Delete # lines. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[M == ESC[1M - if (es_argc != 1) return; - if (es_argv[0] > Info.dwSize.Y - Info.dwCursorPosition.Y) - es_argv[0] = Info.dwSize.Y - Info.dwCursorPosition.Y; - Rect.Left = 0; - Rect.Top = Info.dwCursorPosition.Y + es_argv[0]; - Rect.Right = Info.dwSize.X - 1; - Rect.Bottom = Info.dwSize.Y - 1; - Pos.X = 0; - Pos.Y = Info.dwCursorPosition.Y; - CharInfo.Char.UnicodeChar = ' '; - CharInfo.Attributes = Info.wAttributes; - ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo ); - return; - - case 'P': // ESC[#P Delete # characters. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[P == ESC[1P - if (es_argc != 1) return; - if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1) - es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X; - Rect.Left = Info.dwCursorPosition.X + es_argv[0]; - Rect.Top = Info.dwCursorPosition.Y; - Rect.Right = Info.dwSize.X - 1; - Rect.Bottom = Info.dwCursorPosition.Y; - CharInfo.Char.UnicodeChar = ' '; - CharInfo.Attributes = Info.wAttributes; - ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Info.dwCursorPosition, - &CharInfo ); - return; - - case '@': // ESC[#@ Insert # blank characters. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[@ == ESC[1@ - if (es_argc != 1) return; - if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1) - es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X; - Rect.Left = Info.dwCursorPosition.X; - Rect.Top = Info.dwCursorPosition.Y; - Rect.Right = Info.dwSize.X - 1 - es_argv[0]; - Rect.Bottom = Info.dwCursorPosition.Y; - Pos.X = Info.dwCursorPosition.X + es_argv[0]; - Pos.Y = Info.dwCursorPosition.Y; - CharInfo.Char.UnicodeChar = ' '; - CharInfo.Attributes = Info.wAttributes; - ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo ); - return; - - case 'k': // ESC[#k - case 'A': // ESC[#A Moves cursor up # lines - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[A == ESC[1A - if (es_argc != 1) return; - Pos.Y = Info.dwCursorPosition.Y - es_argv[0]; - if (Pos.Y < 0) Pos.Y = 0; - Pos.X = Info.dwCursorPosition.X; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 'e': // ESC[#e - case 'B': // ESC[#B Moves cursor down # lines - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[B == ESC[1B - if (es_argc != 1) return; - Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; - if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; - Pos.X = Info.dwCursorPosition.X; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 'a': // ESC[#a - case 'C': // ESC[#C Moves cursor forward # spaces - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[C == ESC[1C - if (es_argc != 1) return; - Pos.X = Info.dwCursorPosition.X + es_argv[0]; - if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; - Pos.Y = Info.dwCursorPosition.Y; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 'j': // ESC[#j - case 'D': // ESC[#D Moves cursor back # spaces - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[D == ESC[1D - if (es_argc != 1) return; - Pos.X = Info.dwCursorPosition.X - es_argv[0]; - if (Pos.X < 0) Pos.X = 0; - Pos.Y = Info.dwCursorPosition.Y; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 'E': // ESC[#E Moves cursor down # lines, column 1. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[E == ESC[1E - if (es_argc != 1) return; - Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; - if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; - Pos.X = 0; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 'F': // ESC[#F Moves cursor up # lines, column 1. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[F == ESC[1F - if (es_argc != 1) return; - Pos.Y = Info.dwCursorPosition.Y - es_argv[0]; - if (Pos.Y < 0) Pos.Y = 0; - Pos.X = 0; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case '`': // ESC[#` - case 'G': // ESC[#G Moves cursor column # in current row. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[G == ESC[1G - if (es_argc != 1) return; - Pos.X = es_argv[0] - 1; - if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; - if (Pos.X < 0) Pos.X = 0; - Pos.Y = Info.dwCursorPosition.Y; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 'd': // ESC[#d Moves cursor row #, current column. - if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[d == ESC[1d - if (es_argc != 1) return; - Pos.Y = es_argv[0] - 1; - if (Pos.Y < 0) Pos.Y = 0; - if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 'f': // ESC[#;#f - case 'H': // ESC[#;#H Moves cursor to line #, column # - if (es_argc == 0) - es_argv[es_argc++] = 1; // ESC[H == ESC[1;1H - if (es_argc == 1) - es_argv[es_argc++] = 1; // ESC[#H == ESC[#;1H - if (es_argc > 2) return; - Pos.X = es_argv[1] - 1; - if (Pos.X < 0) Pos.X = 0; - if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; - Pos.Y = es_argv[0] - 1; - if (Pos.Y < 0) Pos.Y = 0; - if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; - SetConsoleCursorPosition( hConOut, Pos ); - return; - - case 's': // ESC[s Saves cursor position for recall later - if (es_argc != 0) return; - SavePos = Info.dwCursorPosition; - return; - - case 'u': // ESC[u Return to saved cursor position - if (es_argc != 0) return; - SetConsoleCursorPosition( hConOut, SavePos ); - return; - - case 'n': // ESC[#n Device status report - if (es_argc != 1) return; // ESC[n == ESC[0n -> ignored - switch (es_argv[0]) - { - case 5: // ESC[5n Report status - SendSequence( L"\33[0n" ); // "OK" - return; - - case 6: // ESC[6n Report cursor position - { - TCHAR buf[32]; - wsprintf( buf, L"\33[%d;%dR", Info.dwCursorPosition.Y + 1, - Info.dwCursorPosition.X + 1 ); - SendSequence( buf ); - } - return; - - default: - return; - } - - case 't': // ESC[#t Window manipulation - if (es_argc != 1) return; - if (es_argv[0] == 21) // ESC[21t Report xterm window's title - { - TCHAR buf[MAX_PATH*2]; - DWORD len = GetConsoleTitle( buf+3, lenof(buf)-3-2 ); - // Too bad if it's too big or fails. - buf[0] = ESC; - buf[1] = ']'; - buf[2] = 'l'; - buf[3+len] = ESC; - buf[3+len+1] = '\\'; - buf[3+len+2] = '\0'; - SendSequence( buf ); - } - return; - - default: - return; - } - } - else // (prefix == ']') - { - // Ignore any \e]? or \e]> sequences. - if (prefix2 != 0) - return; - - if (es_argc == 1 && es_argv[0] == 0) // ESC]0;titleST - { - SetConsoleTitle( Pt_arg ); - } - } -} - -//----------------------------------------------------------------------------- -// ParseAndPrintString(hDev, lpBuffer, nNumberOfBytesToWrite) -// Parses the string lpBuffer, interprets the escapes sequences and prints the -// characters in the device hDev (console). -// The lexer is a three states automata. -// If the number of arguments es_argc > MAX_ARG, only the MAX_ARG-1 firsts and -// the last arguments are processed (no es_argv[] overflow). -//----------------------------------------------------------------------------- - -BOOL -ParseAndPrintString( HANDLE hDev, - LPCVOID lpBuffer, - DWORD nNumberOfBytesToWrite, - LPDWORD lpNumberOfBytesWritten - ) -{ - DWORD i; - LPCTSTR s; - - if (hDev != hConOut) // reinit if device has changed - { - hConOut = hDev; - state = 1; - shifted = FALSE; - } - for (i = nNumberOfBytesToWrite, s = (LPCTSTR)lpBuffer; i > 0; i--, s++) - { - if (state == 1) - { - if (*s == ESC) state = 2; - else if (*s == SO) shifted = TRUE; - else if (*s == SI) shifted = FALSE; - else PushBuffer( *s ); - } - else if (state == 2) - { - if (*s == ESC) ; // \e\e...\e == \e - else if ((*s == '[') || (*s == ']')) - { - FlushBuffer(); - prefix = *s; - prefix2 = 0; - state = 3; - Pt_len = 0; - *Pt_arg = '\0'; - } - else if (*s == ')' || *s == '(') state = 6; - else state = 1; - } - else if (state == 3) - { - if (isdigit( *s )) - { - es_argc = 0; - es_argv[0] = *s - '0'; - state = 4; - } - else if (*s == ';') - { - es_argc = 1; - es_argv[0] = 0; - es_argv[1] = 0; - state = 4; - } - else if (*s == '?' || *s == '>') - { - prefix2 = *s; - } - else - { - es_argc = 0; - suffix = *s; - InterpretEscSeq(); - state = 1; - } - } - else if (state == 4) - { - if (isdigit( *s )) - { - es_argv[es_argc] = 10 * es_argv[es_argc] + (*s - '0'); - } - else if (*s == ';') - { - if (es_argc < MAX_ARG-1) es_argc++; - es_argv[es_argc] = 0; - if (prefix == ']') - state = 5; - } - else - { - es_argc++; - suffix = *s; - InterpretEscSeq(); - state = 1; - } - } - else if (state == 5) - { - if (*s == BEL) - { - Pt_arg[Pt_len] = '\0'; - InterpretEscSeq(); - state = 1; - } - else if (*s == '\\' && Pt_len > 0 && Pt_arg[Pt_len-1] == ESC) - { - Pt_arg[--Pt_len] = '\0'; - InterpretEscSeq(); - state = 1; - } - else if (Pt_len < lenof(Pt_arg)-1) - Pt_arg[Pt_len++] = *s; - } - else if (state == 6) - { - // Ignore it (ESC ) 0 is implicit; nothing else is supported). - state = 1; - } - } - FlushBuffer(); - if (lpNumberOfBytesWritten != NULL) - *lpNumberOfBytesWritten = nNumberOfBytesToWrite - i; - return (i == 0); -} - - -// ========== Hooking API functions -// -// References about API hooking (and dll injection): -// - Matt Pietrek ~ Windows 95 System Programming Secrets. -// - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. - -// Macro for adding pointers/DWORDs together without C arithmetic interfering -#define MakeVA( cast, offset ) (cast)((DWORD_PTR)(pDosHeader)+(DWORD)(offset)) - - -const char APIKernel[] = "kernel32.dll"; -const char APIConsole[] = "API-MS-Win-Core-Console-"; -const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-"; -const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-"; -const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-"; -const char APIFile[] = "API-MS-Win-Core-File-"; - -typedef struct -{ - PCSTR name; - DWORD len; - HMODULE base; -} API_DATA, *PAPI_DATA; - -API_DATA APIs[] = -{ - { APIConsole, sizeof(APIConsole) - 1, NULL }, - { APIProcessThreads, sizeof(APIProcessThreads) - 1, NULL }, - { APIProcessEnvironment, sizeof(APIProcessEnvironment) - 1, NULL }, - { APILibraryLoader, sizeof(APILibraryLoader) - 1, NULL }, - { APIFile, sizeof(APIFile) - 1, NULL }, - { NULL, 0, NULL } -}; - - -HMODULE hKernel; // Kernel32 module handle -HINSTANCE hDllInstance; // Dll instance handle -TCHAR hDllName[MAX_PATH]; // Dll file name -#if defined(_WIN64) || defined(W32ON64) -LPTSTR hDllNameType; // pointer to process type within above -#endif - -typedef struct -{ - PCSTR lib; - PSTR name; - PROC newfunc; - PROC oldfunc; - PROC apifunc; -} HookFn, *PHookFn; - -HookFn Hooks[]; - -const WCHAR zIgnoring[] = L"Ignoring"; -const WCHAR zHooking[] = L"Hooking"; -const WCHAR zUnhooking[] = L"Unhooking"; - - -//----------------------------------------------------------------------------- -// HookAPIOneMod -// Substitute a new function in the Import Address Table (IAT) of the -// specified module. -// Return FALSE on error and TRUE on success. -//----------------------------------------------------------------------------- - -BOOL HookAPIOneMod( - HMODULE hFromModule, // Handle of the module to intercept calls from - PHookFn Hooks, // Functions to replace - BOOL restore // Restore the original functions - ) -{ - PIMAGE_DOS_HEADER pDosHeader; - PIMAGE_NT_HEADERS pNTHeader; - PIMAGE_IMPORT_DESCRIPTOR pImportDesc; - PIMAGE_THUNK_DATA pThunk; - PHookFn hook; - - // Tests to make sure we're looking at a module image (the 'MZ' header) - pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; - if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) - { - DEBUGSTR( 1, L"Image has no DOS header!" ); - return FALSE; - } - - // The MZ header has a pointer to the PE header - pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); - - // One more test to make sure we're looking at a "PE" image - if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) - { - DEBUGSTR( 1, L"Image has no NT header!" ); - return FALSE; - } - - // We now have a valid pointer to the module's PE header. - // Get a pointer to its imports section. - pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR, - pNTHeader->OptionalHeader. - DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. - VirtualAddress ); - - // Bail out if the RVA of the imports section is 0 (it doesn't exist) - if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader) - return TRUE; - - // Iterate through the array of imported module descriptors, looking - // for the module whose name matches the pszFunctionModule parameter. - for (; pImportDesc->Name; pImportDesc++) - { - BOOL kernel = TRUE; - PSTR pszModName = MakeVA( PSTR, pImportDesc->Name ); - if (_stricmp( pszModName, APIKernel ) != 0) - { - PAPI_DATA lib; - for (lib = APIs; lib->name; ++lib) - { - if (_strnicmp( pszModName, lib->name, lib->len ) == 0) - { - if (lib->base == NULL) - { - lib->base = GetModuleHandleA( pszModName ); - for (hook = Hooks; hook->name; ++hook) - if (hook->lib == lib->name) - hook->apifunc = GetProcAddress( lib->base, hook->name ); - } - break; - } - } - if (lib->name == NULL) - { - if (log_level & 16) - DEBUGSTR( 2, L" %s %S", zIgnoring, pszModName ); - continue; - } - kernel = FALSE; - } - if (log_level & 16) - DEBUGSTR( 2, L" Scanning %S", pszModName ); - - // Get a pointer to the found module's import address table (IAT). - pThunk = MakeVA( PIMAGE_THUNK_DATA, pImportDesc->FirstThunk ); - - // Blast through the table of import addresses, looking for the ones - // that match the original addresses. - while (pThunk->u1.Function) - { - for (hook = Hooks; hook->name; ++hook) - { - PROC patch = 0; - if (restore) - { - if ((PROC)pThunk->u1.Function == hook->newfunc) - patch = (kernel) ? hook->oldfunc : hook->apifunc; - } - else if ((PROC)pThunk->u1.Function == hook->oldfunc || - (PROC)pThunk->u1.Function == hook->apifunc) - { - patch = hook->newfunc; - } - if (patch) - { - DWORD flOldProtect, flNewProtect, flDummy; - MEMORY_BASIC_INFORMATION mbi; - - DEBUGSTR( 3, L" %S", hook->name ); - // Get the current protection attributes. - VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) ); - // Take the access protection flags. - flNewProtect = mbi.Protect; - // Remove ReadOnly and ExecuteRead flags. - flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); - // Add on ReadWrite flag - flNewProtect |= (PAGE_READWRITE); - // Change the access protection on the region of committed pages in the - // virtual address space of the current process. - VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), - flNewProtect, &flOldProtect ); - - // Overwrite the original address with the address of the new function. - if (!WriteProcessMemory( GetCurrentProcess(), - &pThunk->u1.Function, - &patch, sizeof(patch), NULL )) - { - DEBUGSTR( 1, L"Could not patch!" ); - return FALSE; - } - - // Put the page attributes back the way they were. - VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), - flOldProtect, &flDummy ); - } - } - pThunk++; // Advance to next imported function address - } - } - - return TRUE; // Function not found -} - -//----------------------------------------------------------------------------- -// HookAPIAllMod -// Substitute a new function in the Import Address Table (IAT) of all -// the modules in the current process. -// Return FALSE on error and TRUE on success. -//----------------------------------------------------------------------------- - -BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore ) -{ - HANDLE hModuleSnap; - MODULEENTRY32 me; - BOOL fOk; - - // Take a snapshot of all modules in the current process. - hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, - GetCurrentProcessId() ); - - if (hModuleSnap == INVALID_HANDLE_VALUE) - { - DEBUGSTR( 1, L"Failed to create snapshot!" ); - return FALSE; - } - - // Fill the size of the structure before using it. - me.dwSize = sizeof(MODULEENTRY32); - - // Walk the module list of the modules. - for (fOk = Module32First( hModuleSnap, &me ); fOk; - fOk = Module32Next( hModuleSnap, &me )) - { - // We don't hook functions in our own module. - if (me.hModule != hDllInstance && me.hModule != hKernel) - { - if (search_env( L"ANSICON_EXC", me.szModule )) - { - DEBUGSTR( 2, L"%s %s", zIgnoring, me.szModule ); - continue; - } - DEBUGSTR( 2, L"%s %s", (restore) ? zUnhooking : zHooking, me.szModule ); - // Hook this function in this module. - if (!HookAPIOneMod( me.hModule, Hooks, restore )) - { - CloseHandle( hModuleSnap ); - return FALSE; - } - } - } - CloseHandle( hModuleSnap ); - DEBUGSTR( 2, L"%s completed", (restore) ? zUnhooking : zHooking ); - return TRUE; -} - - -// ========== Child process injection - -// Inject code into the target process to load our DLL. -void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, - LPPROCESS_INFORMATION child_pi, - BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) -{ - int type; - BOOL gui; - - type = ProcessType( child_pi, &gui ); - if (gui) - { - TCHAR app[MAX_PATH]; - LPTSTR name; - LPCTSTR term = L" \t"; - - app[MAX_PATH-1] = '\0'; - if (lpApp == NULL) - { - // Extract the program from the command line. I would use - // GetModuleFileNameEx, but it doesn't work when a process is created - // suspended and setting up a delay until it does work sometimes - // prevents the process running at all. GetProcessImageFileName works, - // but it's not supported in 2K. - if (wide) - { - LPCTSTR pos; - for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; - if (*pos == '"') - { - term = L"\""; - ++pos; - } - wcsncpy( app, pos, MAX_PATH-1 ); - } - else - { - LPCSTR pos; - for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; - if (*pos == '"') - { - term = L"\""; - ++pos; - } - MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_PATH ); - } - // CreateProcess only works with surrounding quotes ('"a name"' works, but - // 'a" "name' fails), so that's all I'll test, too. However, it also - // tests for a file at each separator ('a name' tries "a.exe" before - // "a name.exe") which I won't do. - name = wcspbrk( app, term ); - if (name) - *name = '\0'; - } - else - { - if (wide) - wcsncpy( app, lpApp, MAX_PATH-1 ); - else - MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_PATH ); - } - name = get_program_name( app ); - if (!search_env( L"ANSICON_GUI", name )) - { - DEBUGSTR( 1, L" %s", zIgnoring ); - type = 0; - } - } - if (type != 0) - { -#ifdef _WIN64 - if (type == 32) - { - hDllNameType[0] = '3'; - hDllNameType[1] = '2'; - InjectDLL32( child_pi, hDllName ); - } - else - { - hDllNameType[0] = '6'; - hDllNameType[1] = '4'; - InjectDLL64( child_pi, hDllName ); - } -#else -#ifdef W32ON64 - if (type == 64) - { - TCHAR args[64]; - STARTUPINFO si; - PROCESS_INFORMATION pi; - wcscpy( hDllNameType, L"CON.exe" ); - wsprintf( args, L"ansicon -P%lu:%lu", - child_pi->dwProcessId, child_pi->dwThreadId ); - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - if (CreateProcess( hDllName, args, NULL, NULL, FALSE, 0, NULL, NULL, - &si, &pi )) - { - WaitForSingleObject( pi.hProcess, INFINITE ); - CloseHandle( pi.hProcess ); - CloseHandle( pi.hThread ); - } - else - DEBUGSTR( 1, L"Could not execute \"%s\"", hDllName ); - wcscpy( hDllNameType, L"32.dll" ); - } - else -#endif - InjectDLL32( child_pi, hDllName ); -#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)) - ResumeThread( child_pi->hThread ); - - if (lpi) - { - memcpy( lpi, child_pi, sizeof(PROCESS_INFORMATION) ); - } - else - { - CloseHandle( child_pi->hThread ); - CloseHandle( child_pi->hProcess ); - } -} - - -BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName, - LPSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCSTR lpCurrentDirectory, - LPSTARTUPINFOA lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation ) -{ - PROCESS_INFORMATION child_pi; - - if (!CreateProcessA( lpApplicationName, - lpCommandLine, - lpThreadAttributes, - lpProcessAttributes, - bInheritHandles, - dwCreationFlags | CREATE_SUSPENDED, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - &child_pi )) - return FALSE; - - DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", \"%S\"", - child_pi.dwProcessId, - (lpApplicationName == NULL) ? "" : lpApplicationName, - (lpCommandLine == NULL) ? "" : lpCommandLine ); - Inject( dwCreationFlags, lpProcessInformation, &child_pi, - FALSE, lpApplicationName, lpCommandLine ); - - return TRUE; -} - - -BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName, - LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation ) -{ - PROCESS_INFORMATION child_pi; - - if (!CreateProcessW( lpApplicationName, - lpCommandLine, - lpThreadAttributes, - lpProcessAttributes, - bInheritHandles, - dwCreationFlags | CREATE_SUSPENDED, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - &child_pi )) - return FALSE; - - DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", \"%s\"", - child_pi.dwProcessId, - (lpApplicationName == NULL) ? L"" : lpApplicationName, - (lpCommandLine == NULL) ? L"" : lpCommandLine ); - Inject( dwCreationFlags, lpProcessInformation, &child_pi, - TRUE, lpApplicationName, lpCommandLine ); - - return TRUE; -} - - -FARPROC WINAPI MyGetProcAddress( HMODULE hModule, LPCSTR lpProcName ) -{ - PHookFn hook; - FARPROC proc; - - proc = GetProcAddress( hModule, lpProcName ); - - if (proc) - { - if (hModule == hKernel) - { - // Ignore LoadLibrary so other hooks continue to work (our version - // might end up at a different address). - if (proc == Hooks[0].oldfunc || proc == Hooks[1].oldfunc) - { - DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName ); - return proc; - } - for (hook = Hooks + 2; hook->name; ++hook) - { - if (proc == hook->oldfunc) - { - DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName ); - return hook->newfunc; - } - } - } - else - { - PAPI_DATA api; - for (api = APIs; api->name; ++api) - { - if (hModule == api->base) - { - if (proc == Hooks[0].apifunc || proc == Hooks[1].apifunc) - { - DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName ); - return proc; - } - for (hook = Hooks + 2; hook->name; ++hook) - { - if (proc == hook->apifunc) - { - DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName ); - return hook->newfunc; - } - } - break; - } - } - } - } - - return proc; -} - - -void HookLibrary( HMODULE hMod, LPCVOID lpFileName, BOOL wide, LPCSTR funcName ) -{ - LPCWSTR name; - WCHAR wname[MAX_PATH]; - - if (hMod && hMod != hKernel) - { - if (!wide) - { - MultiByteToWideChar( AreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, - lpFileName, -1, wname, MAX_PATH ); - lpFileName = wname; - } - name = wcsrchr( lpFileName, '\\' ); - if (name == NULL) - name = lpFileName; - else - ++name; - if (search_env( L"ANSICON_EXC", name )) - DEBUGSTR( 2, L"%s %s (%S)", zIgnoring, lpFileName, funcName ); - else - { - DEBUGSTR( 2, L"%s %s (%S)", zHooking, lpFileName, funcName ); - HookAPIOneMod( hMod, Hooks, FALSE ); - } - } -} - - -HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName ) -{ - HMODULE hMod = LoadLibraryA( lpFileName ); - HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryA" ); - return hMod; -} - - -HMODULE WINAPI MyLoadLibraryW( LPCWSTR lpFileName ) -{ - HMODULE hMod = LoadLibraryW( lpFileName ); - HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryW" ); - return hMod; -} - - -HMODULE WINAPI MyLoadLibraryExA( LPCSTR lpFileName, HANDLE hFile, - DWORD dwFlags ) -{ - HMODULE hMod = LoadLibraryExA( lpFileName, hFile, dwFlags ); - if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) - HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryExA" ); - return hMod; -} - - -HMODULE WINAPI MyLoadLibraryExW( LPCWSTR lpFileName, HANDLE hFile, - DWORD dwFlags ) -{ - HMODULE hMod = LoadLibraryExW( lpFileName, hFile, dwFlags ); - if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) - HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryExW" ); - return hMod; -} - - -//----------------------------------------------------------------------------- -// MyWrite... -// It is the new function that must replace the original Write... function. -// This function have exactly the same signature as the original one. -//----------------------------------------------------------------------------- - -BOOL -WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer, - DWORD nNumberOfCharsToWrite, - LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved ) -{ - DWORD Mode; - LPWSTR buf; - DWORD len; - BOOL rc = TRUE; - - // if we write in a console buffer with processed output - if (GetConsoleMode( hCon, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) - { - UINT cp = GetConsoleOutputCP(); - DEBUGSTR( 4, L"\33WriteConsoleA: %lu \"%.*S\"", - nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); - len = MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, NULL,0 ); - buf = malloc( TSIZE(len) ); - if (buf == NULL) - { - if (lpNumberOfCharsWritten != NULL) - *lpNumberOfCharsWritten = 0; - return (nNumberOfCharsToWrite == 0); - } - MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, buf, len ); - rc = ParseAndPrintString( hCon, buf, len, lpNumberOfCharsWritten ); - free( buf ); - if (rc && lpNumberOfCharsWritten != NULL && - *lpNumberOfCharsWritten != nNumberOfCharsToWrite) - { - // Converting a multibyte character to Unicode results in a different - // "character" count. This causes some programs to think not everything - // was written, so the difference is sent again. Fudge the (presumably) - // correct count. - if (search_env( L"ANSICON_API", prog )) - *lpNumberOfCharsWritten = nNumberOfCharsToWrite; - } - return rc; - } - - return WriteConsoleA( hCon, lpBuffer, nNumberOfCharsToWrite, - lpNumberOfCharsWritten, lpReserved ); - -} - -BOOL -WINAPI MyWriteConsoleW( HANDLE hCon, LPCVOID lpBuffer, - DWORD nNumberOfCharsToWrite, - LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved ) -{ - DWORD Mode; - if (GetConsoleMode( hCon, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) - { - DEBUGSTR( 4, L"\33WriteConsoleW: %lu \"%.*s\"", - nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); - return ParseAndPrintString( hCon, lpBuffer, - nNumberOfCharsToWrite, - lpNumberOfCharsWritten ); - } - - return WriteConsoleW( hCon, lpBuffer, nNumberOfCharsToWrite, - lpNumberOfCharsWritten, lpReserved ); -} - -BOOL -WINAPI MyWriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, - LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped ) -{ - DWORD Mode; - if (GetConsoleMode( hFile, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) - { - DEBUGSTR( 4, L"WriteFile->" ); - return MyWriteConsoleA( hFile, lpBuffer, - nNumberOfBytesToWrite, - lpNumberOfBytesWritten, - lpOverlapped ); - } - - // here, WriteFile is the old function (this module is not hooked) - return WriteFile( hFile, lpBuffer, nNumberOfBytesToWrite, - lpNumberOfBytesWritten, lpOverlapped ); -} - - -#define HHFILE (HANDLE)(DWORD_PTR) - -UINT -WINAPI My_lwrite( HFILE hFile, LPCSTR lpBuffer, UINT uBytes ) -{ - DWORD Mode, written; - if (GetConsoleMode( HHFILE hFile, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) - { - DEBUGSTR( 4, L"_lwrite->" ); - MyWriteConsoleA( HHFILE hFile, lpBuffer, uBytes, &written, NULL ); - return written; - } - - return _lwrite( hFile, lpBuffer, uBytes ); -} - -long -WINAPI My_hwrite( HFILE hFile, LPCSTR lpBuffer, long lBytes ) -{ - DWORD Mode, written; - if (GetConsoleMode( HHFILE hFile, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT)) - { - DEBUGSTR( 4, L"_hwrite->" ); - MyWriteConsoleA( HHFILE hFile, lpBuffer, lBytes, &written, NULL ); - return written; - } - - return _hwrite( hFile, lpBuffer, lBytes ); -} - - -// ========== Environment variable - -void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi ) -{ - CONSOLE_SCREEN_BUFFER_INFO csbi; - TCHAR buf[64]; - - if (pcsbi == NULL) - { - HANDLE hConOut; - hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, 0 ); - GetConsoleScreenBufferInfo( hConOut, &csbi ); - CloseHandle( hConOut ); - pcsbi = &csbi; - } - - wsprintf( buf, L"%dx%d (%dx%d)", - pcsbi->dwSize.X, pcsbi->dwSize.Y, - pcsbi->srWindow.Right - pcsbi->srWindow.Left + 1, - pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1 ); - SetEnvironmentVariable( L"ANSICON", buf ); -} - -DWORD -WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) -{ - if (lstrcmpiA( lpName, "ANSICON_VER" ) == 0) - { - if (nSize < sizeof(PVEREA)) - return sizeof(PVEREA); - memcpy( lpBuffer, PVEREA, sizeof(PVEREA) ); - return sizeof(PVEREA) - 1; - } - - if (lstrcmpiA( lpName, "ANSICON" ) == 0) - set_ansicon( NULL ); - - return GetEnvironmentVariableA( lpName, lpBuffer, nSize ); -} - -DWORD -WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize ) -{ - if (lstrcmpi( lpName, L"ANSICON_VER" ) == 0) - { - if (nSize < lenof(PVERE)) - return lenof(PVERE); - memcpy( lpBuffer, PVERE, sizeof(PVERE) ); - return lenof(PVERE) - 1; - } - - if (lstrcmpi( lpName, L"ANSICON" ) == 0) - set_ansicon( NULL ); - - return GetEnvironmentVariableW( lpName, lpBuffer, nSize ); -} - - -// ========== Initialisation - -HookFn Hooks[] = { - // These two are expected first! - { APILibraryLoader, "LoadLibraryA", (PROC)MyLoadLibraryA, NULL, NULL }, - { APILibraryLoader, "LoadLibraryW", (PROC)MyLoadLibraryW, NULL, NULL }, - { APIProcessThreads, "CreateProcessA", (PROC)MyCreateProcessA, NULL, NULL }, - { APIProcessThreads, "CreateProcessW", (PROC)MyCreateProcessW, NULL, NULL }, - { APIProcessEnvironment, "GetEnvironmentVariableA", (PROC)MyGetEnvironmentVariableA, NULL, NULL }, - { APIProcessEnvironment, "GetEnvironmentVariableW", (PROC)MyGetEnvironmentVariableW, NULL, NULL }, - { APILibraryLoader, "GetProcAddress", (PROC)MyGetProcAddress, NULL, NULL }, - { APILibraryLoader, "LoadLibraryExA", (PROC)MyLoadLibraryExA, NULL, NULL }, - { APILibraryLoader, "LoadLibraryExW", (PROC)MyLoadLibraryExW, NULL, NULL }, - { APIConsole, "WriteConsoleA", (PROC)MyWriteConsoleA, NULL, NULL }, - { APIConsole, "WriteConsoleW", (PROC)MyWriteConsoleW, NULL, NULL }, - { APIFile, "WriteFile", (PROC)MyWriteFile, NULL, NULL }, - { APIKernel, "_lwrite", (PROC)My_lwrite, NULL, NULL }, - { APIKernel, "_hwrite", (PROC)My_hwrite, NULL, NULL }, - { NULL, NULL, NULL, NULL, NULL } -}; - -//----------------------------------------------------------------------------- -// OriginalAttr() -// Determine the original attributes for use by \e[m. -//----------------------------------------------------------------------------- -void OriginalAttr( void ) -{ - HANDLE hConOut; - CONSOLE_SCREEN_BUFFER_INFO csbi; - - hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, 0 ); - if (!GetConsoleScreenBufferInfo( hConOut, &csbi )) - csbi.wAttributes = 7; - CloseHandle( hConOut ); - - 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 ); -} - - -//----------------------------------------------------------------------------- -// DllMain() -// Function called by the system when processes and threads are initialized -// and terminated. -//----------------------------------------------------------------------------- - -__declspec(dllexport) // to stop MinGW exporting everything -BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) -{ - BOOL bResult = TRUE; - PHookFn hook; - TCHAR logstr[4]; - - if (dwReason == DLL_PROCESS_ATTACH) - { - *logstr = '\0'; - GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); - log_level = _wtoi( logstr ); - prog = get_program_name( NULL ); -#if defined(_WIN64) || defined(W32ON64) - hDllNameType = hDllName - 6 + -#endif - GetModuleFileName( hInstance, hDllName, lenof(hDllName) ); - - hDllInstance = hInstance; // save Dll instance handle - DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance ); - - // Get the entry points to the original functions. - hKernel = GetModuleHandleA( APIKernel ); - for (hook = Hooks; hook->name; ++hook) - hook->oldfunc = GetProcAddress( hKernel, hook->name ); - - bResult = HookAPIAllMod( Hooks, FALSE ); - OriginalAttr(); - DisableThreadLibraryCalls( hInstance ); - } - else if (dwReason == DLL_PROCESS_DETACH) - { - if (lpReserved == NULL) - { - DEBUGSTR( 1, L"Unloading" ); - HookAPIAllMod( Hooks, TRUE ); - } - else - { - DEBUGSTR( 1, L"Terminating" ); - s_pid = GetCurrentProcessId(); - s_grm = grm; - s_flag = GRM_EXIT; - } - } - - return bResult; -} +/* + ANSI.c - ANSI escape sequence console driver. + + Jason Hood, 21 & 22 October, 2005. + + Derived from ANSI.xs by Jean-Louis Morel, from his Perl package + Win32::Console::ANSI. I removed the codepage conversion ("\e(") and added + WriteConsole hooking. + + v1.01, 11 & 12 March, 2006: + disable when console has disabled processed output; + \e[5m (blink) is the same as \e[4m (underline); + do not conceal control characters (0 to 31); + \e[m will restore original color. + + v1.10, 22 February, 2009: + fix MyWriteConsoleW for strings longer than the buffer; + initialise attributes to current; + hook into child processes. + + v1.11, 28 February, 2009: + fix hooking into child processes (only do console executables). + + v1.12, 9 March, 2009: + really fix hooking (I didn't realise MinGW didn't generate relocations). + + v1.13, 21 & 27 March, 2009: + alternate injection method, to work with DEP; + use Unicode and the current output code page (not OEMCP). + + v1.14, 3 April, 2009: + fix test for empty import section. + + v1.15, 17 May, 2009: + properly update lpNumberOfCharsWritten in MyWriteConsoleA. + + v1.20, 26 & 29 May, 17 to 21 June, 2009: + create an ANSICON environment variable; + hook GetEnvironmentVariable to create ANSICON dynamically; + use another injection method. + + v1.22, 5 October, 2009: + hook LoadLibrary to intercept the newly loaded functions. + + v1.23, 11 November, 2009: + unload gracefully; + conceal characters by making foreground same as background; + reverse the bold/underline attributes, too. + + v1.25, 15, 20 & 21 July, 2010: + hook LoadLibraryEx (now cscript works); + Win7 support. + + v1.30, 3 August to 7 September, 2010: + x64 support. + + v1.31, 13 & 19 November, 2010: + fix multibyte conversion problems. + + v1.32, 4 to 22 December, 2010: + test for lpNumberOfCharsWritten/lpNumberOfBytesWritten being NULL; + recognise DSR and xterm window title; + ignore sequences starting with \e[? & \e[>; + close the handles opened by CreateProcess. + + v1.40, 25 & 26 February, 1 March, 2011: + hook GetProcAddress, addresses issues with .NET (work with PowerShell); + implement SO & SI to use the DEC Special Graphics Character Set (enables + line drawing via ASCII); ignore \e(X & \e)X (where X is any character); + add \e[?25h & \e[?25l to show/hide the cursor (DECTCEM). + + v1.50, 7 to 14 December, 2011: + added dynamic environment variable ANSICON_VER to return version; + read ANSICON_EXC environment variable to exclude selected modules; + read ANSICON_GUI environment variable to hook selected GUI programs; + read ANSICON_DEF environment variable to set the default GR; + transfer current GR to child, read it on exit. + + v1.51, 15 January, 5, 22 & 24 February, 2012: + added log mask 16 to log all the imported modules of imported modules; + ignore the version within the core API DLL names; + fix 32-bit process trying to identify 64-bit process; + hook _lwrite & _hwrite. + + v1.52, 10 April, 1 & 2 June, 2012: + use ansicon.exe to enable 32-bit to inject into 64-bit; + implement \e[39m & \e[49m (only setting color, nothing else); + added the character/line equivalents (keaj`) of the cursor movement + sequences (ABCDG), as well as vertical absolute (d) and erase characters + (X). + + v1.53, 12 June, 2012: + fixed Update_GRM when running multiple processes (e.g. "cl /MP"). + + v1.60, 22 to 24 November, 2012: + alternative method to obtain LLW for 64->32 injection; + support for VC6 (remove section pragma, rename isdigit to is_digit). + + v1.61, 14 February, 2013: + go back to using ANSI-LLW.exe for 64->32 injection. + + v1.62, 17 & 18 July, 2013: + another method to obtain LLW for 64->32 injection. + + v1.64, 2 August, 2013: + better method of determining a console handle (see IsConsoleHandle). + + v1.65, 28 August, 2013: + fix \e[K (was using window, not buffer). +*/ + +#include "ansicon.h" +#include "version.h" +#include + +#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$ + +#define ESC '\x1B' // ESCape character +#define BEL '\x07' +#define SO '\x0E' // Shift Out +#define SI '\x0F' // Shift In + +#define MAX_ARG 16 // max number of args in an escape sequence +int state; // automata state +TCHAR prefix; // escape sequence prefix ( '[', ']' or '(' ); +TCHAR prefix2; // secondary prefix ( '?' or '>' ); +TCHAR suffix; // escape sequence suffix +int es_argc; // escape sequence args count +int es_argv[MAX_ARG]; // escape sequence args +TCHAR Pt_arg[MAX_PATH*2]; // text parameter for Operating System Command +int Pt_len; +BOOL shifted; + + +// DEC Special Graphics Character Set from +// http://vt100.net/docs/vt220-rm/table2-4.html +// Some of these may not look right, depending on the font and code page (in +// particular, the Control Pictures probably won't work at all). +const WCHAR G1[] = +{ + ' ', // _ - blank + L'\x2666', // ` - Black Diamond Suit + L'\x2592', // a - Medium Shade + L'\x2409', // b - HT + L'\x240c', // c - FF + L'\x240d', // d - CR + L'\x240a', // e - LF + L'\x00b0', // f - Degree Sign + L'\x00b1', // g - Plus-Minus Sign + L'\x2424', // h - NL + L'\x240b', // i - VT + L'\x2518', // j - Box Drawings Light Up And Left + L'\x2510', // k - Box Drawings Light Down And Left + L'\x250c', // l - Box Drawings Light Down And Right + L'\x2514', // m - Box Drawings Light Up And Right + L'\x253c', // n - Box Drawings Light Vertical And Horizontal + L'\x00af', // o - SCAN 1 - Macron + L'\x25ac', // p - SCAN 3 - Black Rectangle + L'\x2500', // q - SCAN 5 - Box Drawings Light Horizontal + L'_', // r - SCAN 7 - Low Line + L'_', // s - SCAN 9 - Low Line + L'\x251c', // t - Box Drawings Light Vertical And Right + L'\x2524', // u - Box Drawings Light Vertical And Left + L'\x2534', // v - Box Drawings Light Up And Horizontal + L'\x252c', // w - Box Drawings Light Down And Horizontal + L'\x2502', // x - Box Drawings Light Vertical + L'\x2264', // y - Less-Than Or Equal To + L'\x2265', // z - Greater-Than Or Equal To + L'\x03c0', // { - Greek Small Letter Pi + L'\x2260', // | - Not Equal To + L'\x00a3', // } - Pound Sign + L'\x00b7', // ~ - Middle Dot +}; + +#define FIRST_G1 '_' +#define LAST_G1 '~' + + +// color constants + +#define FOREGROUND_BLACK 0 +#define FOREGROUND_WHITE FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE + +#define BACKGROUND_BLACK 0 +#define BACKGROUND_WHITE BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE + +const BYTE foregroundcolor[8] = +{ + FOREGROUND_BLACK, // black foreground + FOREGROUND_RED, // red foreground + FOREGROUND_GREEN, // green foreground + FOREGROUND_RED | FOREGROUND_GREEN, // yellow foreground + FOREGROUND_BLUE, // blue foreground + FOREGROUND_BLUE | FOREGROUND_RED, // magenta foreground + FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan foreground + FOREGROUND_WHITE // white foreground +}; + +const BYTE backgroundcolor[8] = +{ + BACKGROUND_BLACK, // black background + BACKGROUND_RED, // red background + BACKGROUND_GREEN, // green background + BACKGROUND_RED | BACKGROUND_GREEN, // yellow background + BACKGROUND_BLUE, // blue background + BACKGROUND_BLUE | BACKGROUND_RED, // magenta background + BACKGROUND_BLUE | BACKGROUND_GREEN, // cyan background + BACKGROUND_WHITE, // white background +}; + +const BYTE attr2ansi[8] = // map console attribute to ANSI number +{ + 0, // black + 4, // blue + 2, // green + 6, // cyan + 1, // red + 5, // magenta + 3, // yellow + 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 + +#ifdef _WIN64 +SHARED DWORD LLW32; +#endif + + +// 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; + HANDLE proc = ((LPPROCESS_INFORMATION)child_pi)->hProcess; + free( child_pi ); + + WaitForSingleObject( proc, INFINITE ); + CloseHandle( proc ); + + if (s_flag == GRM_EXIT && s_pid == pid) + { + s_flag = 0; + grm = s_grm; + } + + return 0; +} + + +// Search an environment variable for a string. +BOOL search_env( LPCTSTR var, LPCTSTR val ) +{ + static LPTSTR env; + static DWORD env_len; + DWORD len; + BOOL not; + + len = GetEnvironmentVariable( var, env, env_len ); + if (len == 0) + return FALSE; + + if (len > env_len) + { + LPTSTR tmp = realloc( env, TSIZE(len) ); + if (tmp == NULL) + return FALSE; + env = tmp; + env_len = len; + GetEnvironmentVariable( var, env, env_len ); + } + + not = (*env == '!'); + if (not && env[1] == '\0') + return TRUE; + + for (var = wcstok( env + not, L";" ); var; var = wcstok( NULL, L";" )) + if (_wcsicmp( val, var ) == 0) + return !not; + + return not; +} + + +// ========== Print Buffer functions + +#define BUFFER_SIZE 2048 + +int nCharInBuffer; +WCHAR ChBuffer[BUFFER_SIZE]; + +//----------------------------------------------------------------------------- +// FlushBuffer() +// Writes the buffer to the console and empties it. +//----------------------------------------------------------------------------- + +void FlushBuffer( void ) +{ + DWORD nWritten; + if (nCharInBuffer <= 0) return; + WriteConsole( hConOut, ChBuffer, nCharInBuffer, &nWritten, NULL ); + nCharInBuffer = 0; +} + +//----------------------------------------------------------------------------- +// PushBuffer( WCHAR c ) +// Adds a character in the buffer. +//----------------------------------------------------------------------------- + +void PushBuffer( WCHAR c ) +{ + if (shifted && c >= FIRST_G1 && c <= LAST_G1) + c = G1[c-FIRST_G1]; + ChBuffer[nCharInBuffer] = c; + if (++nCharInBuffer == BUFFER_SIZE) + FlushBuffer(); +} + +//----------------------------------------------------------------------------- +// SendSequence( LPTSTR seq ) +// Send the string to the input buffer. +//----------------------------------------------------------------------------- + +void SendSequence( LPTSTR seq ) +{ + DWORD out; + INPUT_RECORD in; + HANDLE hStdIn = GetStdHandle( STD_INPUT_HANDLE ); + + in.EventType = KEY_EVENT; + in.Event.KeyEvent.bKeyDown = TRUE; + in.Event.KeyEvent.wRepeatCount = 1; + in.Event.KeyEvent.wVirtualKeyCode = 0; + in.Event.KeyEvent.wVirtualScanCode = 0; + in.Event.KeyEvent.dwControlKeyState = 0; + for (; *seq; ++seq) + { + in.Event.KeyEvent.uChar.UnicodeChar = *seq; + WriteConsoleInput( hStdIn, &in, 1, &out ); + } +} + +// ========== Print functions + +//----------------------------------------------------------------------------- +// InterpretEscSeq() +// Interprets the last escape sequence scanned by ParseAndPrintString +// prefix escape sequence prefix +// es_argc escape sequence args count +// es_argv[] escape sequence args array +// suffix escape sequence suffix +// +// for instance, with \e[33;45;1m we have +// prefix = '[', +// es_argc = 3, es_argv[0] = 33, es_argv[1] = 45, es_argv[2] = 1 +// suffix = 'm' +//----------------------------------------------------------------------------- + +void InterpretEscSeq( void ) +{ + int i; + WORD attribut; + CONSOLE_SCREEN_BUFFER_INFO Info; + CONSOLE_CURSOR_INFO CursInfo; + DWORD len, NumberOfCharsWritten; + COORD Pos; + SMALL_RECT Rect; + CHAR_INFO CharInfo; + + if (prefix == '[') + { + if (prefix2 == '?' && (suffix == 'h' || suffix == 'l')) + { + if (es_argc == 1 && es_argv[0] == 25) + { + GetConsoleCursorInfo( hConOut, &CursInfo ); + CursInfo.bVisible = (suffix == 'h'); + SetConsoleCursorInfo( hConOut, &CursInfo ); + return; + } + } + // Ignore any other \e[? or \e[> sequences. + if (prefix2 != 0) + return; + + GetConsoleScreenBufferInfo( hConOut, &Info ); + switch (suffix) + { + case 'm': + 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; + else if (40 <= es_argv[i] && es_argv[i] <= 47) + grm.background = es_argv[i] - 40; + else switch (es_argv[i]) + { + case 0: + case 39: + case 49: + { + TCHAR def[4]; + int a; + *def = '7'; def[1] = '\0'; + GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) ); + a = wcstol( def, NULL, 16 ); + grm.reverse = FALSE; + if (a < 0) + { + grm.reverse = TRUE; + a = -a; + } + if (es_argv[i] != 49) + grm.foreground = attr2ansi[a & 7]; + if (es_argv[i] != 39) + grm.background = attr2ansi[(a >> 4) & 7]; + if (es_argv[i] == 0) + { + if (es_argc == 1) + { + grm.bold = a & FOREGROUND_INTENSITY; + grm.underline = a & BACKGROUND_INTENSITY; + } + else + { + grm.bold = 0; + grm.underline = 0; + } + grm.rvideo = 0; + grm.concealed = 0; + } + } + break; + + case 1: grm.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 21: // oops, this actually turns on double underline + case 22: grm.bold = 0; break; + case 25: + case 24: grm.underline = 0; break; + case 27: grm.rvideo = 0; break; + case 28: grm.concealed = 0; break; + } + } + if (grm.concealed) + { + if (grm.rvideo) + { + attribut = foregroundcolor[grm.foreground] + | backgroundcolor[grm.foreground]; + if (grm.bold) + attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } + else + { + attribut = foregroundcolor[grm.background] + | backgroundcolor[grm.background]; + if (grm.underline) + attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } + } + else if (grm.rvideo) + { + attribut = foregroundcolor[grm.background] + | backgroundcolor[grm.foreground]; + if (grm.bold) + attribut |= BACKGROUND_INTENSITY; + if (grm.underline) + attribut |= FOREGROUND_INTENSITY; + } + else + attribut = foregroundcolor[grm.foreground] | grm.bold + | backgroundcolor[grm.background] | grm.underline; + if (grm.reverse) + attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4); + SetConsoleTextAttribute( hConOut, attribut ); + return; + + case 'J': + if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[J == ESC[0J + if (es_argc != 1) return; + switch (es_argv[0]) + { + case 0: // ESC[0J erase from cursor to end of display + len = (Info.dwSize.Y - Info.dwCursorPosition.Y - 1) * Info.dwSize.X + + Info.dwSize.X - Info.dwCursorPosition.X - 1; + FillConsoleOutputCharacter( hConOut, ' ', len, + Info.dwCursorPosition, + &NumberOfCharsWritten ); + FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, + Info.dwCursorPosition, + &NumberOfCharsWritten ); + return; + + case 1: // ESC[1J erase from start to cursor. + Pos.X = 0; + Pos.Y = 0; + len = Info.dwCursorPosition.Y * Info.dwSize.X + + Info.dwCursorPosition.X + 1; + FillConsoleOutputCharacter( hConOut, ' ', len, Pos, + &NumberOfCharsWritten ); + FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, Pos, + &NumberOfCharsWritten ); + return; + + case 2: // ESC[2J Clear screen and home cursor + Pos.X = 0; + Pos.Y = 0; + len = Info.dwSize.X * Info.dwSize.Y; + FillConsoleOutputCharacter( hConOut, ' ', len, Pos, + &NumberOfCharsWritten ); + FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, Pos, + &NumberOfCharsWritten ); + SetConsoleCursorPosition( hConOut, Pos ); + return; + + default: + return; + } + + case 'K': + if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[K == ESC[0K + if (es_argc != 1) return; + switch (es_argv[0]) + { + case 0: // ESC[0K Clear to end of line + len = Info.dwSize.X - Info.dwCursorPosition.X + 1; + FillConsoleOutputCharacter( hConOut, ' ', len, + Info.dwCursorPosition, + &NumberOfCharsWritten ); + FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, + Info.dwCursorPosition, + &NumberOfCharsWritten ); + return; + + case 1: // ESC[1K Clear from start of line to cursor + Pos.X = 0; + Pos.Y = Info.dwCursorPosition.Y; + FillConsoleOutputCharacter( hConOut, ' ', + Info.dwCursorPosition.X + 1, Pos, + &NumberOfCharsWritten ); + FillConsoleOutputAttribute( hConOut, Info.wAttributes, + Info.dwCursorPosition.X + 1, Pos, + &NumberOfCharsWritten ); + return; + + case 2: // ESC[2K Clear whole line. + Pos.X = 0; + Pos.Y = Info.dwCursorPosition.Y; + FillConsoleOutputCharacter( hConOut, ' ', Info.dwSize.X, Pos, + &NumberOfCharsWritten ); + FillConsoleOutputAttribute( hConOut, Info.wAttributes, + Info.dwSize.X, Pos, + &NumberOfCharsWritten ); + return; + + default: + return; + } + + case 'X': // ESC[#X Erase # characters. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[X == ESC[1X + if (es_argc != 1) return; + FillConsoleOutputCharacter( hConOut, ' ', es_argv[0], + Info.dwCursorPosition, + &NumberOfCharsWritten ); + FillConsoleOutputAttribute( hConOut, Info.wAttributes, es_argv[0], + Info.dwCursorPosition, + &NumberOfCharsWritten ); + return; + + case 'L': // ESC[#L Insert # blank lines. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[L == ESC[1L + if (es_argc != 1) return; + Rect.Left = 0; + Rect.Top = Info.dwCursorPosition.Y; + Rect.Right = Info.dwSize.X - 1; + Rect.Bottom = Info.dwSize.Y - 1; + Pos.X = 0; + Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; + CharInfo.Char.UnicodeChar = ' '; + CharInfo.Attributes = Info.wAttributes; + ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo ); + return; + + case 'M': // ESC[#M Delete # lines. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[M == ESC[1M + if (es_argc != 1) return; + if (es_argv[0] > Info.dwSize.Y - Info.dwCursorPosition.Y) + es_argv[0] = Info.dwSize.Y - Info.dwCursorPosition.Y; + Rect.Left = 0; + Rect.Top = Info.dwCursorPosition.Y + es_argv[0]; + Rect.Right = Info.dwSize.X - 1; + Rect.Bottom = Info.dwSize.Y - 1; + Pos.X = 0; + Pos.Y = Info.dwCursorPosition.Y; + CharInfo.Char.UnicodeChar = ' '; + CharInfo.Attributes = Info.wAttributes; + ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo ); + return; + + case 'P': // ESC[#P Delete # characters. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[P == ESC[1P + if (es_argc != 1) return; + if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1) + es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X; + Rect.Left = Info.dwCursorPosition.X + es_argv[0]; + Rect.Top = Info.dwCursorPosition.Y; + Rect.Right = Info.dwSize.X - 1; + Rect.Bottom = Info.dwCursorPosition.Y; + CharInfo.Char.UnicodeChar = ' '; + CharInfo.Attributes = Info.wAttributes; + ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Info.dwCursorPosition, + &CharInfo ); + return; + + case '@': // ESC[#@ Insert # blank characters. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[@ == ESC[1@ + if (es_argc != 1) return; + if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1) + es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X; + Rect.Left = Info.dwCursorPosition.X; + Rect.Top = Info.dwCursorPosition.Y; + Rect.Right = Info.dwSize.X - 1 - es_argv[0]; + Rect.Bottom = Info.dwCursorPosition.Y; + Pos.X = Info.dwCursorPosition.X + es_argv[0]; + Pos.Y = Info.dwCursorPosition.Y; + CharInfo.Char.UnicodeChar = ' '; + CharInfo.Attributes = Info.wAttributes; + ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo ); + return; + + case 'k': // ESC[#k + case 'A': // ESC[#A Moves cursor up # lines + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[A == ESC[1A + if (es_argc != 1) return; + Pos.Y = Info.dwCursorPosition.Y - es_argv[0]; + if (Pos.Y < 0) Pos.Y = 0; + Pos.X = Info.dwCursorPosition.X; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 'e': // ESC[#e + case 'B': // ESC[#B Moves cursor down # lines + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[B == ESC[1B + if (es_argc != 1) return; + Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; + if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; + Pos.X = Info.dwCursorPosition.X; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 'a': // ESC[#a + case 'C': // ESC[#C Moves cursor forward # spaces + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[C == ESC[1C + if (es_argc != 1) return; + Pos.X = Info.dwCursorPosition.X + es_argv[0]; + if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; + Pos.Y = Info.dwCursorPosition.Y; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 'j': // ESC[#j + case 'D': // ESC[#D Moves cursor back # spaces + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[D == ESC[1D + if (es_argc != 1) return; + Pos.X = Info.dwCursorPosition.X - es_argv[0]; + if (Pos.X < 0) Pos.X = 0; + Pos.Y = Info.dwCursorPosition.Y; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 'E': // ESC[#E Moves cursor down # lines, column 1. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[E == ESC[1E + if (es_argc != 1) return; + Pos.Y = Info.dwCursorPosition.Y + es_argv[0]; + if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; + Pos.X = 0; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 'F': // ESC[#F Moves cursor up # lines, column 1. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[F == ESC[1F + if (es_argc != 1) return; + Pos.Y = Info.dwCursorPosition.Y - es_argv[0]; + if (Pos.Y < 0) Pos.Y = 0; + Pos.X = 0; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case '`': // ESC[#` + case 'G': // ESC[#G Moves cursor column # in current row. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[G == ESC[1G + if (es_argc != 1) return; + Pos.X = es_argv[0] - 1; + if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; + if (Pos.X < 0) Pos.X = 0; + Pos.Y = Info.dwCursorPosition.Y; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 'd': // ESC[#d Moves cursor row #, current column. + if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[d == ESC[1d + if (es_argc != 1) return; + Pos.Y = es_argv[0] - 1; + if (Pos.Y < 0) Pos.Y = 0; + if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 'f': // ESC[#;#f + case 'H': // ESC[#;#H Moves cursor to line #, column # + if (es_argc == 0) + es_argv[es_argc++] = 1; // ESC[H == ESC[1;1H + if (es_argc == 1) + es_argv[es_argc++] = 1; // ESC[#H == ESC[#;1H + if (es_argc > 2) return; + Pos.X = es_argv[1] - 1; + if (Pos.X < 0) Pos.X = 0; + if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1; + Pos.Y = es_argv[0] - 1; + if (Pos.Y < 0) Pos.Y = 0; + if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1; + SetConsoleCursorPosition( hConOut, Pos ); + return; + + case 's': // ESC[s Saves cursor position for recall later + if (es_argc != 0) return; + SavePos = Info.dwCursorPosition; + return; + + case 'u': // ESC[u Return to saved cursor position + if (es_argc != 0) return; + SetConsoleCursorPosition( hConOut, SavePos ); + return; + + case 'n': // ESC[#n Device status report + if (es_argc != 1) return; // ESC[n == ESC[0n -> ignored + switch (es_argv[0]) + { + case 5: // ESC[5n Report status + SendSequence( L"\33[0n" ); // "OK" + return; + + case 6: // ESC[6n Report cursor position + { + TCHAR buf[32]; + wsprintf( buf, L"\33[%d;%dR", Info.dwCursorPosition.Y + 1, + Info.dwCursorPosition.X + 1 ); + SendSequence( buf ); + } + return; + + default: + return; + } + + case 't': // ESC[#t Window manipulation + if (es_argc != 1) return; + if (es_argv[0] == 21) // ESC[21t Report xterm window's title + { + TCHAR buf[MAX_PATH*2]; + DWORD len = GetConsoleTitle( buf+3, lenof(buf)-3-2 ); + // Too bad if it's too big or fails. + buf[0] = ESC; + buf[1] = ']'; + buf[2] = 'l'; + buf[3+len] = ESC; + buf[3+len+1] = '\\'; + buf[3+len+2] = '\0'; + SendSequence( buf ); + } + return; + + default: + return; + } + } + else // (prefix == ']') + { + // Ignore any \e]? or \e]> sequences. + if (prefix2 != 0) + return; + + if (es_argc == 1 && es_argv[0] == 0) // ESC]0;titleST + { + SetConsoleTitle( Pt_arg ); + } + } +} + +//----------------------------------------------------------------------------- +// ParseAndPrintString(hDev, lpBuffer, nNumberOfBytesToWrite) +// Parses the string lpBuffer, interprets the escapes sequences and prints the +// characters in the device hDev (console). +// The lexer is a three states automata. +// If the number of arguments es_argc > MAX_ARG, only the MAX_ARG-1 firsts and +// the last arguments are processed (no es_argv[] overflow). +//----------------------------------------------------------------------------- + +BOOL +ParseAndPrintString( HANDLE hDev, + LPCVOID lpBuffer, + DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten + ) +{ + DWORD i; + LPCTSTR s; + + if (hDev != hConOut) // reinit if device has changed + { + hConOut = hDev; + state = 1; + shifted = FALSE; + } + for (i = nNumberOfBytesToWrite, s = (LPCTSTR)lpBuffer; i > 0; i--, s++) + { + if (state == 1) + { + if (*s == ESC) state = 2; + else if (*s == SO) shifted = TRUE; + else if (*s == SI) shifted = FALSE; + else PushBuffer( *s ); + } + else if (state == 2) + { + if (*s == ESC) ; // \e\e...\e == \e + else if ((*s == '[') || (*s == ']')) + { + FlushBuffer(); + prefix = *s; + prefix2 = 0; + state = 3; + Pt_len = 0; + *Pt_arg = '\0'; + } + else if (*s == ')' || *s == '(') state = 6; + else state = 1; + } + else if (state == 3) + { + if (is_digit( *s )) + { + es_argc = 0; + es_argv[0] = *s - '0'; + state = 4; + } + else if (*s == ';') + { + es_argc = 1; + es_argv[0] = 0; + es_argv[1] = 0; + state = 4; + } + else if (*s == '?' || *s == '>') + { + prefix2 = *s; + } + else + { + es_argc = 0; + suffix = *s; + InterpretEscSeq(); + state = 1; + } + } + else if (state == 4) + { + if (is_digit( *s )) + { + es_argv[es_argc] = 10 * es_argv[es_argc] + (*s - '0'); + } + else if (*s == ';') + { + if (es_argc < MAX_ARG-1) es_argc++; + es_argv[es_argc] = 0; + if (prefix == ']') + state = 5; + } + else + { + es_argc++; + suffix = *s; + InterpretEscSeq(); + state = 1; + } + } + else if (state == 5) + { + if (*s == BEL) + { + Pt_arg[Pt_len] = '\0'; + InterpretEscSeq(); + state = 1; + } + else if (*s == '\\' && Pt_len > 0 && Pt_arg[Pt_len-1] == ESC) + { + Pt_arg[--Pt_len] = '\0'; + InterpretEscSeq(); + state = 1; + } + else if (Pt_len < lenof(Pt_arg)-1) + Pt_arg[Pt_len++] = *s; + } + else if (state == 6) + { + // Ignore it (ESC ) 0 is implicit; nothing else is supported). + state = 1; + } + } + FlushBuffer(); + if (lpNumberOfBytesWritten != NULL) + *lpNumberOfBytesWritten = nNumberOfBytesToWrite - i; + return (i == 0); +} + + +// ========== Hooking API functions +// +// References about API hooking (and dll injection): +// - Matt Pietrek ~ Windows 95 System Programming Secrets. +// - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. + +// Macro for adding pointers/DWORDs together without C arithmetic interfering +#define MakeVA( cast, offset ) (cast)((DWORD_PTR)(pDosHeader)+(DWORD)(offset)) + + +const char APIKernel[] = "kernel32.dll"; +const char APIConsole[] = "API-MS-Win-Core-Console-"; +const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-"; +const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-"; +const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-"; +const char APIFile[] = "API-MS-Win-Core-File-"; + +typedef struct +{ + PCSTR name; + DWORD len; + HMODULE base; +} API_DATA, *PAPI_DATA; + +API_DATA APIs[] = +{ + { APIConsole, sizeof(APIConsole) - 1, NULL }, + { APIProcessThreads, sizeof(APIProcessThreads) - 1, NULL }, + { APIProcessEnvironment, sizeof(APIProcessEnvironment) - 1, NULL }, + { APILibraryLoader, sizeof(APILibraryLoader) - 1, NULL }, + { APIFile, sizeof(APIFile) - 1, NULL }, + { NULL, 0, NULL } +}; + + +HMODULE hKernel; // Kernel32 module handle +HINSTANCE hDllInstance; // Dll instance handle +TCHAR hDllName[MAX_PATH]; // Dll file name +#if defined(_WIN64) || defined(W32ON64) +LPTSTR hDllNameType; // pointer to process type within above +#endif + +typedef struct +{ + PCSTR lib; + PSTR name; + PROC newfunc; + PROC oldfunc; + PROC apifunc; +} HookFn, *PHookFn; + +HookFn Hooks[]; + +const WCHAR zIgnoring[] = L"Ignoring"; +const WCHAR zHooking[] = L"Hooking"; +const WCHAR zUnhooking[] = L"Unhooking"; + + +//----------------------------------------------------------------------------- +// HookAPIOneMod +// Substitute a new function in the Import Address Table (IAT) of the +// specified module. +// Return FALSE on error and TRUE on success. +//----------------------------------------------------------------------------- + +BOOL HookAPIOneMod( + HMODULE hFromModule, // Handle of the module to intercept calls from + PHookFn Hooks, // Functions to replace + BOOL restore // Restore the original functions + ) +{ + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNTHeader; + PIMAGE_IMPORT_DESCRIPTOR pImportDesc; + PIMAGE_THUNK_DATA pThunk; + PHookFn hook; + + // Tests to make sure we're looking at a module image (the 'MZ' header) + pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + { + DEBUGSTR( 1, L"Image has no DOS header!" ); + return FALSE; + } + + // The MZ header has a pointer to the PE header + pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); + + // One more test to make sure we're looking at a "PE" image + if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) + { + DEBUGSTR( 1, L"Image has no NT header!" ); + return FALSE; + } + + // We now have a valid pointer to the module's PE header. + // Get a pointer to its imports section. + pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR, + pNTHeader->OptionalHeader. + DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. + VirtualAddress ); + + // Bail out if the RVA of the imports section is 0 (it doesn't exist) + if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader) + return TRUE; + + // Iterate through the array of imported module descriptors, looking + // for the module whose name matches the pszFunctionModule parameter. + for (; pImportDesc->Name; pImportDesc++) + { + BOOL kernel = TRUE; + PSTR pszModName = MakeVA( PSTR, pImportDesc->Name ); + if (_stricmp( pszModName, APIKernel ) != 0) + { + PAPI_DATA lib; + for (lib = APIs; lib->name; ++lib) + { + if (_strnicmp( pszModName, lib->name, lib->len ) == 0) + { + if (lib->base == NULL) + { + lib->base = GetModuleHandleA( pszModName ); + for (hook = Hooks; hook->name; ++hook) + if (hook->lib == lib->name) + hook->apifunc = GetProcAddress( lib->base, hook->name ); + } + break; + } + } + if (lib->name == NULL) + { + if (log_level & 16) + DEBUGSTR( 2, L" %s %S", zIgnoring, pszModName ); + continue; + } + kernel = FALSE; + } + if (log_level & 16) + DEBUGSTR( 2, L" Scanning %S", pszModName ); + + // Get a pointer to the found module's import address table (IAT). + pThunk = MakeVA( PIMAGE_THUNK_DATA, pImportDesc->FirstThunk ); + + // Blast through the table of import addresses, looking for the ones + // that match the original addresses. + while (pThunk->u1.Function) + { + for (hook = Hooks; hook->name; ++hook) + { + PROC patch = 0; + if (restore) + { + if ((PROC)pThunk->u1.Function == hook->newfunc) + patch = (kernel) ? hook->oldfunc : hook->apifunc; + } + else if ((PROC)pThunk->u1.Function == hook->oldfunc || + (PROC)pThunk->u1.Function == hook->apifunc) + { + patch = hook->newfunc; + } + if (patch) + { + DWORD flOldProtect, flNewProtect, flDummy; + MEMORY_BASIC_INFORMATION mbi; + + DEBUGSTR( 3, L" %S", hook->name ); + // Get the current protection attributes. + VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) ); + // Take the access protection flags. + flNewProtect = mbi.Protect; + // Remove ReadOnly and ExecuteRead flags. + flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); + // Add on ReadWrite flag + flNewProtect |= (PAGE_READWRITE); + // Change the access protection on the region of committed pages in the + // virtual address space of the current process. + VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), + flNewProtect, &flOldProtect ); + + // Overwrite the original address with the address of the new function. + if (!WriteProcessMemory( GetCurrentProcess(), + &pThunk->u1.Function, + &patch, sizeof(patch), NULL )) + { + DEBUGSTR( 1, L"Could not patch!" ); + return FALSE; + } + + // Put the page attributes back the way they were. + VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), + flOldProtect, &flDummy ); + } + } + pThunk++; // Advance to next imported function address + } + } + + return TRUE; // Function not found +} + +//----------------------------------------------------------------------------- +// HookAPIAllMod +// Substitute a new function in the Import Address Table (IAT) of all +// the modules in the current process. +// Return FALSE on error and TRUE on success. +//----------------------------------------------------------------------------- + +BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore ) +{ + HANDLE hModuleSnap; + MODULEENTRY32 me; + BOOL fOk; + + // Take a snapshot of all modules in the current process. + hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, + GetCurrentProcessId() ); + + if (hModuleSnap == INVALID_HANDLE_VALUE) + { + DEBUGSTR( 1, L"Failed to create snapshot!" ); + return FALSE; + } + + // Fill the size of the structure before using it. + me.dwSize = sizeof(MODULEENTRY32); + + // Walk the module list of the modules. + for (fOk = Module32First( hModuleSnap, &me ); fOk; + fOk = Module32Next( hModuleSnap, &me )) + { + // We don't hook functions in our own module. + if (me.hModule != hDllInstance && me.hModule != hKernel) + { + if (search_env( L"ANSICON_EXC", me.szModule )) + { + DEBUGSTR( 2, L"%s %s", zIgnoring, me.szModule ); + continue; + } + DEBUGSTR( 2, L"%s %s", (restore) ? zUnhooking : zHooking, me.szModule ); + // Hook this function in this module. + if (!HookAPIOneMod( me.hModule, Hooks, restore )) + { + CloseHandle( hModuleSnap ); + return FALSE; + } + } + } + CloseHandle( hModuleSnap ); + DEBUGSTR( 2, L"%s completed", (restore) ? zUnhooking : zHooking ); + return TRUE; +} + + +// ========== Child process injection + +// Inject code into the target process to load our DLL. +void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, + LPPROCESS_INFORMATION child_pi, + BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) +{ + int type; + BOOL gui; + + type = ProcessType( child_pi, &gui ); + if (gui) + { + TCHAR app[MAX_PATH]; + LPTSTR name; + LPCTSTR term = L" \t"; + + app[MAX_PATH-1] = '\0'; + if (lpApp == NULL) + { + // Extract the program from the command line. I would use + // GetModuleFileNameEx, but it doesn't work when a process is created + // suspended and setting up a delay until it does work sometimes + // prevents the process running at all. GetProcessImageFileName works, + // but it's not supported in 2K. + if (wide) + { + LPCTSTR pos; + for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; + if (*pos == '"') + { + term = L"\""; + ++pos; + } + wcsncpy( app, pos, MAX_PATH-1 ); + } + else + { + LPCSTR pos; + for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ; + if (*pos == '"') + { + term = L"\""; + ++pos; + } + MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_PATH ); + } + // CreateProcess only works with surrounding quotes ('"a name"' works, but + // 'a" "name' fails), so that's all I'll test, too. However, it also + // tests for a file at each separator ('a name' tries "a.exe" before + // "a name.exe") which I won't do. + name = wcspbrk( app, term ); + if (name) + *name = '\0'; + } + else + { + if (wide) + wcsncpy( app, lpApp, MAX_PATH-1 ); + else + MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_PATH ); + } + name = get_program_name( app ); + if (!search_env( L"ANSICON_GUI", name )) + { + DEBUGSTR( 1, L" %s", zIgnoring ); + type = 0; + } + } + if (type != 0) + { +#ifdef _WIN64 + if (type == 32) + { + hDllNameType[0] = '3'; + hDllNameType[1] = '2'; + InjectDLL32( child_pi, hDllName ); + } + else + { + hDllNameType[0] = '6'; + hDllNameType[1] = '4'; + InjectDLL64( child_pi, hDllName ); + } +#else +#ifdef W32ON64 + if (type == 64) + { + TCHAR args[64]; + STARTUPINFO si; + PROCESS_INFORMATION pi; + wcscpy( hDllNameType, L"CON.exe" ); + wsprintf( args, L"ansicon -P%lu:%lu", + child_pi->dwProcessId, child_pi->dwThreadId ); + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + if (CreateProcess( hDllName, args, NULL, NULL, FALSE, 0, NULL, NULL, + &si, &pi )) + { + WaitForSingleObject( pi.hProcess, INFINITE ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } + else + DEBUGSTR( 1, L"Could not execute \"%s\"", hDllName ); + wcscpy( hDllNameType, L"32.dll" ); + } + else +#endif + InjectDLL32( child_pi, hDllName ); +#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)) + ResumeThread( child_pi->hThread ); + + if (lpi) + { + memcpy( lpi, child_pi, sizeof(PROCESS_INFORMATION) ); + } + else + { + CloseHandle( child_pi->hThread ); + CloseHandle( child_pi->hProcess ); + } +} + + +BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName, + LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation ) +{ + PROCESS_INFORMATION child_pi; + + if (!CreateProcessA( lpApplicationName, + lpCommandLine, + lpThreadAttributes, + lpProcessAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &child_pi )) + return FALSE; + + DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", \"%S\"", + child_pi.dwProcessId, + (lpApplicationName == NULL) ? "" : lpApplicationName, + (lpCommandLine == NULL) ? "" : lpCommandLine ); + Inject( dwCreationFlags, lpProcessInformation, &child_pi, + FALSE, lpApplicationName, lpCommandLine ); + + return TRUE; +} + + +BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation ) +{ + PROCESS_INFORMATION child_pi; + + if (!CreateProcessW( lpApplicationName, + lpCommandLine, + lpThreadAttributes, + lpProcessAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &child_pi )) + return FALSE; + + DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", \"%s\"", + child_pi.dwProcessId, + (lpApplicationName == NULL) ? L"" : lpApplicationName, + (lpCommandLine == NULL) ? L"" : lpCommandLine ); + Inject( dwCreationFlags, lpProcessInformation, &child_pi, + TRUE, lpApplicationName, lpCommandLine ); + + return TRUE; +} + + +FARPROC WINAPI MyGetProcAddress( HMODULE hModule, LPCSTR lpProcName ) +{ + PHookFn hook; + FARPROC proc; + + proc = GetProcAddress( hModule, lpProcName ); + + if (proc) + { + if (hModule == hKernel) + { + // Ignore LoadLibrary so other hooks continue to work (our version + // might end up at a different address). + if (proc == Hooks[0].oldfunc || proc == Hooks[1].oldfunc) + { + DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName ); + return proc; + } + for (hook = Hooks + 2; hook->name; ++hook) + { + if (proc == hook->oldfunc) + { + DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName ); + return hook->newfunc; + } + } + } + else + { + PAPI_DATA api; + for (api = APIs; api->name; ++api) + { + if (hModule == api->base) + { + if (proc == Hooks[0].apifunc || proc == Hooks[1].apifunc) + { + DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName ); + return proc; + } + for (hook = Hooks + 2; hook->name; ++hook) + { + if (proc == hook->apifunc) + { + DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName ); + return hook->newfunc; + } + } + break; + } + } + } + } + + return proc; +} + + +void HookLibrary( HMODULE hMod, LPCVOID lpFileName, BOOL wide, LPCSTR funcName ) +{ + LPCWSTR name; + WCHAR wname[MAX_PATH]; + + if (hMod && hMod != hKernel) + { + if (!wide) + { + MultiByteToWideChar( AreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, + lpFileName, -1, wname, MAX_PATH ); + lpFileName = wname; + } + name = wcsrchr( lpFileName, '\\' ); + if (name == NULL) + name = lpFileName; + else + ++name; + if (search_env( L"ANSICON_EXC", name )) + DEBUGSTR( 2, L"%s %s (%S)", zIgnoring, lpFileName, funcName ); + else + { + DEBUGSTR( 2, L"%s %s (%S)", zHooking, lpFileName, funcName ); + HookAPIOneMod( hMod, Hooks, FALSE ); + } + } +} + + +HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName ) +{ + HMODULE hMod = LoadLibraryA( lpFileName ); + HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryA" ); + return hMod; +} + + +HMODULE WINAPI MyLoadLibraryW( LPCWSTR lpFileName ) +{ + HMODULE hMod = LoadLibraryW( lpFileName ); + HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryW" ); + return hMod; +} + + +HMODULE WINAPI MyLoadLibraryExA( LPCSTR lpFileName, HANDLE hFile, + DWORD dwFlags ) +{ + HMODULE hMod = LoadLibraryExA( lpFileName, hFile, dwFlags ); + if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) + HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryExA" ); + return hMod; +} + + +HMODULE WINAPI MyLoadLibraryExW( LPCWSTR lpFileName, HANDLE hFile, + DWORD dwFlags ) +{ + HMODULE hMod = LoadLibraryExW( lpFileName, hFile, dwFlags ); + if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) + HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryExW" ); + return hMod; +} + + +//----------------------------------------------------------------------------- +// IsConsoleHandle +// Determine if the handle is writing to the console, with processed output. +//----------------------------------------------------------------------------- +BOOL IsConsoleHandle( HANDLE h ) +{ + DWORD mode; + + if (!GetConsoleMode( h, &mode )) + { + // This fails if the handle isn't opened for reading. Fortunately, it + // seems WriteConsole tests the handle before it tests the length. + return WriteConsole( h, NULL, 0, &mode, NULL ); + } + + return (mode & ENABLE_PROCESSED_OUTPUT); +} + + +//----------------------------------------------------------------------------- +// MyWrite... +// The new functions that must replace the original Write... functions. These +// functions have exactly the same signature as the original ones. This +// module is not hooked, so we can still call the original functions ourselves. +//----------------------------------------------------------------------------- + +BOOL +WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer, + DWORD nNumberOfCharsToWrite, + LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved ) +{ + LPWSTR buf; + DWORD len; + BOOL rc = TRUE; + + if (IsConsoleHandle( hCon )) + { + UINT cp = GetConsoleOutputCP(); + DEBUGSTR( 4, L"\33WriteConsoleA: %lu \"%.*S\"", + nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); + len = MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, NULL,0 ); + buf = malloc( TSIZE(len) ); + if (buf == NULL) + { + if (lpNumberOfCharsWritten != NULL) + *lpNumberOfCharsWritten = 0; + return (nNumberOfCharsToWrite == 0); + } + MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, buf, len ); + rc = ParseAndPrintString( hCon, buf, len, lpNumberOfCharsWritten ); + free( buf ); + if (rc && lpNumberOfCharsWritten != NULL && + *lpNumberOfCharsWritten != nNumberOfCharsToWrite) + { + // Converting a multibyte character to Unicode results in a different + // "character" count. This causes some programs to think not everything + // was written, so the difference is sent again. Fudge the (presumably) + // correct count. + if (search_env( L"ANSICON_API", prog )) + *lpNumberOfCharsWritten = nNumberOfCharsToWrite; + } + return rc; + } + + return WriteConsoleA( hCon, lpBuffer, nNumberOfCharsToWrite, + lpNumberOfCharsWritten, lpReserved ); + +} + +BOOL +WINAPI MyWriteConsoleW( HANDLE hCon, LPCVOID lpBuffer, + DWORD nNumberOfCharsToWrite, + LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved ) +{ + if (IsConsoleHandle( hCon )) + { + DEBUGSTR( 4, L"\33WriteConsoleW: %lu \"%.*s\"", + nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); + return ParseAndPrintString( hCon, lpBuffer, + nNumberOfCharsToWrite, + lpNumberOfCharsWritten ); + } + + return WriteConsoleW( hCon, lpBuffer, nNumberOfCharsToWrite, + lpNumberOfCharsWritten, lpReserved ); +} + +BOOL +WINAPI MyWriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped ) +{ + if (IsConsoleHandle( hFile )) + { + DEBUGSTR( 4, L"WriteFile->" ); + return MyWriteConsoleA( hFile, lpBuffer, + nNumberOfBytesToWrite, + lpNumberOfBytesWritten, + lpOverlapped ); + } + + return WriteFile( hFile, lpBuffer, nNumberOfBytesToWrite, + lpNumberOfBytesWritten, lpOverlapped ); +} + + +#define HHFILE (HANDLE)(DWORD_PTR) + +UINT +WINAPI My_lwrite( HFILE hFile, LPCSTR lpBuffer, UINT uBytes ) +{ + if (IsConsoleHandle( HHFILE hFile )) + { + DWORD written; + DEBUGSTR( 4, L"_lwrite->" ); + MyWriteConsoleA( HHFILE hFile, lpBuffer, uBytes, &written, NULL ); + return written; + } + + return _lwrite( hFile, lpBuffer, uBytes ); +} + +long +WINAPI My_hwrite( HFILE hFile, LPCSTR lpBuffer, long lBytes ) +{ + if (IsConsoleHandle( HHFILE hFile )) + { + DWORD written; + DEBUGSTR( 4, L"_hwrite->" ); + MyWriteConsoleA( HHFILE hFile, lpBuffer, lBytes, &written, NULL ); + return written; + } + + return _hwrite( hFile, lpBuffer, lBytes ); +} + + +// ========== Environment variable + +void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi ) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + TCHAR buf[64]; + + if (pcsbi == NULL) + { + HANDLE hConOut; + hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + GetConsoleScreenBufferInfo( hConOut, &csbi ); + CloseHandle( hConOut ); + pcsbi = &csbi; + } + + wsprintf( buf, L"%dx%d (%dx%d)", + pcsbi->dwSize.X, pcsbi->dwSize.Y, + pcsbi->srWindow.Right - pcsbi->srWindow.Left + 1, + pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1 ); + SetEnvironmentVariable( L"ANSICON", buf ); +} + +DWORD +WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) +{ + if (lstrcmpiA( lpName, "ANSICON_VER" ) == 0) + { + if (nSize < sizeof(PVEREA)) + return sizeof(PVEREA); + memcpy( lpBuffer, PVEREA, sizeof(PVEREA) ); + return sizeof(PVEREA) - 1; + } + + if (lstrcmpiA( lpName, "ANSICON" ) == 0) + set_ansicon( NULL ); + + return GetEnvironmentVariableA( lpName, lpBuffer, nSize ); +} + +DWORD +WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize ) +{ + if (lstrcmpi( lpName, L"ANSICON_VER" ) == 0) + { + if (nSize < lenof(PVERE)) + return lenof(PVERE); + memcpy( lpBuffer, PVERE, sizeof(PVERE) ); + return lenof(PVERE) - 1; + } + + if (lstrcmpi( lpName, L"ANSICON" ) == 0) + set_ansicon( NULL ); + + return GetEnvironmentVariableW( lpName, lpBuffer, nSize ); +} + + +// ========== Initialisation + +HookFn Hooks[] = { + // These two are expected first! + { APILibraryLoader, "LoadLibraryA", (PROC)MyLoadLibraryA, NULL, NULL }, + { APILibraryLoader, "LoadLibraryW", (PROC)MyLoadLibraryW, NULL, NULL }, + { APIProcessThreads, "CreateProcessA", (PROC)MyCreateProcessA, NULL, NULL }, + { APIProcessThreads, "CreateProcessW", (PROC)MyCreateProcessW, NULL, NULL }, + { APIProcessEnvironment, "GetEnvironmentVariableA", (PROC)MyGetEnvironmentVariableA, NULL, NULL }, + { APIProcessEnvironment, "GetEnvironmentVariableW", (PROC)MyGetEnvironmentVariableW, NULL, NULL }, + { APILibraryLoader, "GetProcAddress", (PROC)MyGetProcAddress, NULL, NULL }, + { APILibraryLoader, "LoadLibraryExA", (PROC)MyLoadLibraryExA, NULL, NULL }, + { APILibraryLoader, "LoadLibraryExW", (PROC)MyLoadLibraryExW, NULL, NULL }, + { APIConsole, "WriteConsoleA", (PROC)MyWriteConsoleA, NULL, NULL }, + { APIConsole, "WriteConsoleW", (PROC)MyWriteConsoleW, NULL, NULL }, + { APIFile, "WriteFile", (PROC)MyWriteFile, NULL, NULL }, + { APIKernel, "_lwrite", (PROC)My_lwrite, NULL, NULL }, + { APIKernel, "_hwrite", (PROC)My_hwrite, NULL, NULL }, + { NULL, NULL, NULL, NULL, NULL } +}; + +//----------------------------------------------------------------------------- +// OriginalAttr() +// Determine the original attributes for use by \e[m. +//----------------------------------------------------------------------------- +void OriginalAttr( void ) +{ + HANDLE hConOut; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + if (!GetConsoleScreenBufferInfo( hConOut, &csbi )) + csbi.wAttributes = 7; + CloseHandle( hConOut ); + + 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 ); +} + + +//----------------------------------------------------------------------------- +// DllMain() +// Function called by the system when processes and threads are initialized +// and terminated. +//----------------------------------------------------------------------------- + +__declspec(dllexport) // just to stop MinGW exporting everything +BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) +{ + BOOL bResult = TRUE; + PHookFn hook; + TCHAR logstr[4]; + + if (dwReason == DLL_PROCESS_ATTACH) + { + *logstr = '\0'; + GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); + log_level = _wtoi( logstr ); + prog = get_program_name( NULL ); +#if defined(_WIN64) || defined(W32ON64) + hDllNameType = hDllName - 6 + +#endif + GetModuleFileName( hInstance, hDllName, lenof(hDllName) ); + + hDllInstance = hInstance; // save Dll instance handle + DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance ); + + // Get the entry points to the original functions. + hKernel = GetModuleHandleA( APIKernel ); + for (hook = Hooks; hook->name; ++hook) + hook->oldfunc = GetProcAddress( hKernel, hook->name ); + + bResult = HookAPIAllMod( Hooks, FALSE ); + OriginalAttr(); + DisableThreadLibraryCalls( hInstance ); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + if (lpReserved == NULL) + { + DEBUGSTR( 1, L"Unloading" ); + HookAPIAllMod( Hooks, TRUE ); + } + else + { + DEBUGSTR( 1, L"Terminating" ); + s_pid = GetCurrentProcessId(); + s_grm = grm; + s_flag = GRM_EXIT; + } + } + + return bResult; +} diff --git a/ansicon/COPYING.MinGW-w64-runtime.txt b/ansicon/COPYING.MinGW-w64-runtime.txt deleted file mode 100755 index 3d11e87..0000000 --- a/ansicon/COPYING.MinGW-w64-runtime.txt +++ /dev/null @@ -1,240 +0,0 @@ -MinGW-w64 runtime licensing -*************************** - -This program or library was built using MinGW-w64 and statically -linked against the MinGW-w64 runtime. Some parts of the runtime -are under licenses which require that the copyright and license -notices are included when distributing the code in binary form. -These notices are listed below. - - -======================== -Overall copyright notice -======================== - -Copyright (c) 2009, 2010 by the mingw-w64 project - -This license has been certified as open source. It has also been designated -as GPL compatible by the Free Software Foundation (FSF). - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions in source code must retain the accompanying copyright - notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the accompanying - copyright notice, this list of conditions, and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - 3. Names of the copyright holders must not be used to endorse or promote - products derived from this software without prior written permission - from the copyright holders. - 4. The right to distribute this software or to use it for any purpose does - not give you the right to use Servicemarks (sm) or Trademarks (tm) of - the copyright holders. Use of them is covered by separate agreement - with the copyright holders. - 5. If any files are modified, you must cause the modified files to carry - prominent notices stating that you changed the files and the date of - any change. - -Disclaimer - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -======================================== -getopt, getopt_long, and getop_long_only -======================================== - -Copyright (c) 2002 Todd C. Miller - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Sponsored in part by the Defense Advanced Research Projects -Agency (DARPA) and Air Force Research Laboratory, Air Force -Materiel Command, USAF, under agreement number F39502-99-1-0512. - - * * * * * * * - -Copyright (c) 2000 The NetBSD Foundation, Inc. -All rights reserved. - -This code is derived from software contributed to The NetBSD Foundation -by Dieter Baron and Thomas Klausner. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - -=============================================================== -gdtoa: Converting between IEEE floating point numbers and ASCII -=============================================================== - -The author of this software is David M. Gay. - -Copyright (C) 1997, 1998, 1999, 2000, 2001 by Lucent Technologies -All Rights Reserved - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appear in all -copies and that both that the copyright notice and this -permission notice and warranty disclaimer appear in supporting -documentation, and that the name of Lucent or any of its entities -not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. -IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY -SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER -IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -THIS SOFTWARE. - - * * * * * * * - -The author of this software is David M. Gay. - -Copyright (C) 2005 by David M. Gay -All Rights Reserved - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that the copyright notice and this permission notice and warranty -disclaimer appear in supporting documentation, and that the name of -the author or any of his current or former employers not be used in -advertising or publicity pertaining to distribution of the software -without specific, written prior permission. - -THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN -NO EVENT SHALL THE AUTHOR OR ANY OF HIS CURRENT OR FORMER EMPLOYERS BE -LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS -SOFTWARE. - - * * * * * * * - -The author of this software is David M. Gay. - -Copyright (C) 2004 by David M. Gay. -All Rights Reserved -Based on material in the rest of /netlib/fp/gdota.tar.gz, -which is copyright (C) 1998, 2000 by Lucent Technologies. - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appear in all -copies and that both that the copyright notice and this -permission notice and warranty disclaimer appear in supporting -documentation, and that the name of Lucent or any of its entities -not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. -IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY -SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER -IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -THIS SOFTWARE. - - -========================= -Parts of the math library -========================= - -Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - -Developed at SunSoft, a Sun Microsystems, Inc. business. -Permission to use, copy, modify, and distribute this -software is freely granted, provided that this notice -is preserved. - - * * * * * * * - -Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - -Developed at SunPro, a Sun Microsystems, Inc. business. -Permission to use, copy, modify, and distribute this -software is freely granted, provided that this notice -is preserved. - - * * * * * * * - -FIXME: Cephes math lib -Copyright (C) 1984-1998 Stephen L. Moshier - -It sounds vague, but as to be found at -, it gives an -impression that the author could be willing to give an explicit -permission to distribute those files e.g. under a BSD style license. So -probably there is no problem here, although it could be good to get a -permission from the author and then add a license into the Cephes files -in MinGW runtime. At least on follow-up it is marked that debian sees the -version a-like BSD one. As MinGW.org (where those cephes parts are coming -from) distributes them now over 6 years, it should be fine. - -=================================== -Headers and IDLs imported from Wine -=================================== - -Some header and IDL files were imported from the Wine project. These files -are prominent maked in source. Their copyright belongs to contributors and -they are distributed under LGPL license. - -Disclaimer - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library 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 -Lesser General Public License for more details. diff --git a/ansicon/G1.bat b/ansicon/G1.bat old mode 100755 new mode 100644 index 37a6bb8..88b4fdb --- a/ansicon/G1.bat +++ b/ansicon/G1.bat @@ -1,18 +1,18 @@ -@echo off & setlocal - -::Extract the current code page. Hopefully this method will work with other -::languages. CHCP outputs: "Active code page: #". Take the last five -::characters (the longest code) and delete up to and including the space. -for /f "delims=" %%j in ('chcp') do set CP=%%j -set CP=%CP:~-5% -set CP=%CP:* =% - -x86\ansicon -e The DEC Special Graphics Character Set according to code page %CP%:^ - -^ - - _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~^ - -^ - - _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~ +@echo off & setlocal + +::Extract the current code page. Hopefully this method will work with other +::languages. CHCP outputs: "Active code page: #". Take the last five +::characters (the longest code) and delete up to and including the space. +for /f "delims=" %%j in ('chcp') do set CP=%%j +set CP=%CP:~-5% +set CP=%CP:* =% + +x86\ansicon -e The DEC Special Graphics Character Set according to code page %CP%:^ + +^ + + _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~^ + +^ + + _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~ diff --git a/ansicon/G1.txt b/ansicon/G1.txt old mode 100755 new mode 100644 diff --git a/ansicon/LICENSE.txt b/ansicon/LICENSE.txt new file mode 100644 index 0000000..bc565e3 --- /dev/null +++ b/ansicon/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (C) 2005-2013 Jason Hood + +This software is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Jason Hood +jadoxa@yahoo.com.au diff --git a/ansicon/ansi.rc b/ansicon/ansi.rc old mode 100755 new mode 100644 index 16a4585..18e9eec --- a/ansicon/ansi.rc +++ b/ansicon/ansi.rc @@ -1,42 +1,36 @@ -/* - ansi.rc - Version resource for ANSI{32,64}.dll. - - Jason Hood, 11 November, 2009. -*/ - -#include -#include "version.h" - -#ifdef _WIN64 -# define BITS "64" -#else -# define BITS "32" -#endif - -1 VERSIONINFO -FILEVERSION PVERB -PRODUCTVERSION PVERB -FILEOS VOS_NT -FILETYPE VFT_DLL -{ - BLOCK "StringFileInfo" - { - BLOCK "040904B0" - { - VALUE "Comments", "http://ansicon.adoxa.cjb.net/" - VALUE "CompanyName", "Jason Hood" - VALUE "FileDescription", "ANSI Console" - VALUE "FileVersion", PVERSA - VALUE "InternalName", "ANSI" BITS - VALUE "LegalCopyright", "Freeware" - VALUE "OriginalFilename", "ANSI" BITS ".dll" - VALUE "ProductName", "ANSICON" - VALUE "ProductVersion", PVERSA - } - } - - BLOCK "VarFileInfo" - { - VALUE "Translation", 0x0409, 0x04B0 - } -} +/* + ansi.rc - Version resource for ANSI{32,64}.dll. + + Jason Hood, 11 November, 2009. +*/ + +#include +#include "version.h" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION PVERB +PRODUCTVERSION PVERB +FILEOS VOS_NT +FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" + { + BLOCK "040904B0" + { + VALUE "Comments", "http://ansicon.adoxa.vze.com/" + VALUE "CompanyName", "Jason Hood" + VALUE "FileDescription", "ANSI Console" + VALUE "FileVersion", PVERSA + VALUE "InternalName", "ANSI" BITS + VALUE "LegalCopyright", "Freeware" + VALUE "OriginalFilename", ANSIDLL + VALUE "ProductName", "ANSICON" + VALUE "ProductVersion", PVERSA + } + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04B0 + } +} diff --git a/ansicon/ansicon.c b/ansicon/ansicon.c old mode 100755 new mode 100644 index 2b87abe..4368a55 --- a/ansicon/ansicon.c +++ b/ansicon/ansicon.c @@ -1,686 +1,833 @@ -/* - ANSICON.c - ANSI escape sequence console driver. - - Jason Hood, 21 to 23 October, 2005. - - Original injection code was derived from Console Manager by Sergey Oblomov - (hoopoepg). Use of FlushInstructionCache came from www.catch22.net. - Additional information came from "Process-wide API spying - an ultimate hack", - Anton Bassov's article in "The Code Project" (use of OpenThread). - - v1.01, 11 & 12 March, 2006: - -m option to set "monochrome" (grey on black); - restore original color on exit. - - v1.10, 22 February, 2009: - ignore Ctrl+C/Ctrl+Break. - - v1.13, 21 & 27 March, 2009: - alternate injection method, to work with DEP; - use Unicode. - - v1.20, 17 to 21 June, 2009: - use a combination of the two injection methods; - test if ANSICON is already installed; - added -e (and -E) option to echo the command line (without newline); - added -t (and -T) option to type (display) files (with file name). - - v1.21, 23 September, 2009: - added -i (and -u) to add (remove) ANSICON to AutoRun. - - v1.24, 6 & 7 January, 2010: - no arguments to -t, or using "-" for the name, will read from stdin; - fix -t and -e when ANSICON was already loaded. - - v1.25, 22 July, 2010: - added -IU for HKLM. - - v1.30, 3 August to 7 September, 2010: - x64 support. - - v1.31, 13 & 15 November, 2010: - use LLW to fix potential Unicode path problems; - VC compatibility (2008 Express for 32-bit, PSDK 2003 R2 for 64-bit); - explicitly use wide characters (stick with TCHAR, but not ). - - v1.32, 4 to 22 December, 2010: - make -p more robust; - inject into GUI processes; - -i implies -p. - - v1.50, 7 to 14 December, 2011: - -u does not imply -p; - add the PID to the debugging output; - use ANSICON_VER to test if already installed; - always place first in AutoRun; - logging is always available, controlled by ANSICON_LOG environment variable; - only restore the original color after program/echo/type; - return program's exit code. - - 7 January, 2012: - fixed installing into a piped CMD.EXE; - added a log message indicating all imports have been processed. - - v1.52, 10 April, 2012: - fixed running "cmd" if "ComSpec" is not defined; - pass process & thread identifiers on the command line (for x86->x64). -*/ - -#define PDATE L"12 June, 2012" - -#include "ansicon.h" -#include "version.h" -#include -#include -#include - -#ifdef __MINGW32__ -int _CRT_glob = 0; -#endif - - -#ifdef _WIN64 -# define BITS L"64" -#else -# define BITS L"32" -#endif - - -#define CMDKEY L"Software\\Microsoft\\Command Processor" -#define AUTORUN L"AutoRun" - - -void help( void ); - -void display( LPCTSTR, BOOL ); -void print_error( LPCTSTR, ... ); -LPTSTR skip_spaces( LPTSTR ); -void get_arg( LPTSTR, LPTSTR*, LPTSTR* ); - -void process_autorun( TCHAR ); - -BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ); -BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR ); - - -// Find the name of the DLL and inject it. -BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app ) -{ - DWORD len; - WCHAR dll[MAX_PATH]; - int type; - - DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); - type = ProcessType( ppi, gui ); - if (type == 0) - { - fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app ); - return FALSE; - } - - len = (DWORD)(prog - prog_path); - memcpy( dll, prog_path, TSIZE(len) ); -#ifdef _WIN64 - wsprintf( dll + len, L"ANSI%d.dll", type ); - if (type == 32) - InjectDLL32( ppi, dll ); - else - InjectDLL64( ppi, dll ); -#else - wcscpy( dll + len, L"ANSI32.dll" ); - InjectDLL32( ppi, dll ); -#endif - return TRUE; -} - - -static HANDLE hConOut; -static CONSOLE_SCREEN_BUFFER_INFO csbi; - -void get_original_attr( void ) -{ - hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, 0 ); - GetConsoleScreenBufferInfo( hConOut, &csbi ); -} - - -void set_original_attr( void ) -{ - SetConsoleTextAttribute( hConOut, csbi.wAttributes ); - CloseHandle( hConOut ); -} - - -DWORD CtrlHandler( DWORD event ) -{ - return (event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT); -} - - -int main( void ) -{ - STARTUPINFO si; - PROCESS_INFORMATION pi; - LPTSTR argv, arg, cmd; - TCHAR logstr[4]; - BOOL installed; - BOOL shell, run, gui; - HMODULE ansi; - DWORD len; - int rc = 0; - - argv = GetCommandLine(); - len = (DWORD)wcslen( argv ) + 1; - if (len < MAX_PATH) - len = MAX_PATH; - arg = malloc( TSIZE(len) ); - get_arg( arg, &argv, &cmd ); // skip the program name - get_arg( arg, &argv, &cmd ); - - if (*arg) - { - if (wcscmp( arg, L"/?" ) == 0 || - wcscmp( arg, L"--help" ) == 0) - { - help(); - return rc; - } - if (wcscmp( arg, L"--version" ) == 0) - { - _putws( L"ANSICON (" BITS L"-bit) version " PVERS L" (" PDATE L")." ); - return rc; - } - } - - prog = get_program_name( NULL ); - *logstr = '\0'; - GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); - log_level = _wtoi( logstr ); - -#ifdef _WIN64 - if (*arg == '-' && arg[1] == 'P') - { - swscanf( arg + 2, L"%u:%u", &pi.dwProcessId, &pi.dwThreadId ); - pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); - pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); - Inject( &pi, &gui, arg ); - CloseHandle( pi.hThread ); - CloseHandle( pi.hProcess ); - return 0; - } -#endif - - if (log_level && !(log_level & 8)) - DEBUGSTR( 1, NULL ); // create a new file - - installed = (GetEnvironmentVariable( L"ANSICON_VER", NULL, 0 ) != 0); - // If it's already installed, remove it. This serves two purposes: preserves - // the parent's GRM; and unconditionally injects into GUI, without having to - // worry about ANSICON_GUI. - if (installed) - { - fputws( L"\33[m", stdout ); - FreeLibrary( GetModuleHandle( L"ANSI" BITS L".dll" ) ); - } - - shell = run = TRUE; - get_original_attr(); - - while (*arg == '-') - { - switch (arg[1]) - { - case 'l': - SetEnvironmentVariable( L"ANSICON_LOG", arg + 2 ); - log_level = _wtoi( arg + 2 ); - if (!(log_level & 8)) // unless told otherwise - DEBUGSTR( 1, NULL ); // create a new file - break; - - case 'i': - case 'I': - case 'u': - case 'U': - shell = FALSE; - process_autorun( arg[1] ); - if (arg[1] == 'u' || arg[1] == 'U') - break; - // else fall through - - case 'p': - shell = FALSE; - // If it's already installed, there's no need to do anything. - if (installed) - { - DEBUGSTR( 1, L"Already installed" ); - } - else if (GetParentProcessInfo( &pi, arg )) - { - pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); - pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); - SuspendThread( pi.hThread ); - if (!Inject( &pi, &gui, arg )) - rc = 1; - ResumeThread( pi.hThread ); - CloseHandle( pi.hThread ); - CloseHandle( pi.hProcess ); - } - else - { - fputws( L"ANSICON: could not obtain the parent process.\n", stderr ); - rc = 1; - } - break; - - case 'm': - { - int a = wcstol( arg + 2, NULL, 16 ); - if (a == 0) - a = (arg[2] == '-') ? -7 : 7; - if (a < 0) - { - SetEnvironmentVariable( L"ANSICON_REVERSE", L"1" ); - a = -a; - a = ((a >> 4) & 15) | ((a & 15) << 4); - } - SetConsoleTextAttribute( hConOut, a ); - SetEnvironmentVariable( L"ANSICON_DEF", NULL ); - break; - } - - case 'e': - case 'E': - case 't': - case 'T': - run = FALSE; - ++arg; - goto arg_out; - } - get_arg( arg, &argv, &cmd ); - } -arg_out: - if (run && *cmd == '\0') - { - if (!shell) - run = FALSE; - else if (!_isatty( 0 )) - { - *arg = 't'; - run = FALSE; - } - } - - if (run) - { - if (*cmd == '\0') - { - cmd = _wgetenv( L"ComSpec" ); - if (cmd == NULL) - { - // CreateProcessW writes to the string, so can't simply point to "cmd". - static TCHAR cmdstr[] = L"cmd"; - cmd = cmdstr; - } - arg = cmd; - } - - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, - NULL, NULL, &si, &pi )) - { - Inject( &pi, &gui, arg ); - ResumeThread( pi.hThread ); - if (!gui) - { - SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); - WaitForSingleObject( pi.hProcess, INFINITE ); - GetExitCodeProcess( pi.hProcess, (LPDWORD)(LPVOID)&rc ); - } - CloseHandle( pi.hProcess ); - CloseHandle( pi.hThread ); - } - else - { - print_error( arg, arg ); - rc = 1; - } - } - else if (*arg) - { - ansi = LoadLibrary( L"ANSI" BITS L".dll" ); - if (ansi == NULL) - { - print_error( L"ANSI" BITS L".dll" ); - rc = 1; - } - - if (*arg == 'e' || *arg == 'E') - { - cmd += 2; - if (*cmd == ' ' || *cmd == '\t') - ++cmd; - fputws( cmd, stdout ); - if (*arg == 'e') - putwchar( '\n' ); - } - else // (*arg == 't' || *arg == 'T') - { - BOOL title = (*arg == 'T'); - get_arg( arg, &argv, &cmd ); - if (*arg == '\0') - wcscpy( arg, L"-" ); - do - { - if (title) - { - wprintf( L"==> %s <==\n", arg ); - display( arg, title ); - putwchar( '\n' ); - } - else - display( arg, title ); - get_arg( arg, &argv, &cmd ); - } while (*arg); - } - - FreeLibrary( ansi ); - } - - if (run || *arg) - set_original_attr(); - else - CloseHandle( hConOut ); - - return rc; -} - - -// Display a file. -void display( LPCTSTR name, BOOL title ) -{ - HANDLE in, out; - BOOL pipe; - char buf[8192]; - DWORD len; - - if (*name == '-' && name[1] == '\0') - { - pipe = TRUE; - in = GetStdHandle( STD_INPUT_HANDLE ); - } - else - { - pipe = FALSE; - in = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, NULL ); - if (in == INVALID_HANDLE_VALUE) - { - print_error( name ); - return; - } - } - if (title) - { - putwchar( '\n' ); - // Need to flush, otherwise it's written *after* STD_OUTPUT_HANDLE should - // it be redirected. - fflush( stdout ); - } - out = GetStdHandle( STD_OUTPUT_HANDLE ); - for (;;) - { - if (!ReadFile( in, buf, sizeof(buf), &len, NULL )) - { - if (GetLastError() != ERROR_BROKEN_PIPE) - print_error( name ); - break; - } - if (len == 0) - break; - WriteFile( out, buf, len, &len, NULL ); - } - if (!pipe) - CloseHandle( in ); -} - - -void print_error( LPCTSTR name, ... ) -{ - LPTSTR errmsg = NULL; - DWORD err = GetLastError(); - va_list arg; - - if (err == ERROR_BAD_EXE_FORMAT) - { - // This error requires an argument, which is a duplicate of name. - va_start( arg, name ); - FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, &arg ); - va_end( arg ); - fwprintf( stderr, L"ANSICON: %s", errmsg ); - } - else - { - FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ); - // Just in case there are other messages requiring args... - if (errmsg == NULL) - fwprintf( stderr, L"ANSICON: %s: Error %lu.\n", name, err ); - else - fwprintf( stderr, L"ANSICON: %s: %s", name, errmsg ); - } - LocalFree( errmsg ); -} - - -// Add or remove ANSICON to AutoRun. -void process_autorun( TCHAR cmd ) -{ - HKEY cmdkey; - TCHAR ansicon[MAX_PATH+80]; - TCHAR logstr[80]; - LPTSTR autorun, ansirun; - DWORD len, type, exist; - BOOL inst; - - if (log_level) - _snwprintf( logstr, lenof(logstr), L"set ANSICON_LOG=%d&", log_level ); - else - *logstr = '\0'; - len = TSIZE(_snwprintf( ansicon, lenof(ansicon), - L"(if %%ANSICON_VER%%==^%%ANSICON_VER^%% %s\"%s\" -p)", - logstr, prog_path ) + 1); - - inst = (towlower( cmd ) == 'i'); - RegCreateKeyEx( (iswlower( cmd )) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, - CMDKEY, 0, NULL, - REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, - &cmdkey, &exist ); - exist = 0; - RegQueryValueEx( cmdkey, AUTORUN, NULL, NULL, NULL, &exist ); - if (exist == 0) - { - if (inst) - RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); - } - else - { - // Let's assume there's sufficient memory. - autorun = malloc( exist + len ); - RegQueryValueEx( cmdkey, AUTORUN, NULL, &type, (PBYTE)autorun, &exist ); - // Remove the existing command, if present. - ansirun = wcsstr( autorun, L"(if %ANSICON_VER%" ); - if (ansirun != NULL) - { - LPTSTR tmp = wcschr( ansirun, '"' ); // opening quote - tmp = wcschr( tmp + 1, '"' ); // closing quote - tmp = wcschr( tmp + 1, ')' ); // closing bracket - if (*++tmp == '&') - ++tmp; - if (*tmp == '&') - ++tmp; - if (*tmp == '\0') - { - if (ansirun > autorun && ansirun[-1] == '&') - --ansirun; - if (ansirun > autorun && ansirun[-1] == '&') - --ansirun; - } - wcscpy( ansirun, tmp ); - exist = TSIZE((DWORD)wcslen( autorun ) + 1); - } - if (inst) - { - if (exist == sizeof(TCHAR)) - RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); - else - { - memmove( (PBYTE)autorun + len, autorun, exist ); - memcpy( autorun, ansicon, len ); - ((PBYTE)autorun)[len-sizeof(TCHAR)] = '&'; - RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist+len ); - } - } - else - { - if (exist == sizeof(TCHAR)) - RegDeleteValue( cmdkey, AUTORUN ); - else - RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist ); - } - free( autorun ); - } - RegCloseKey( cmdkey ); -} - - -// Search each process in the snapshot for id. -BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ) -{ - BOOL fOk; - - ppe->dwSize = sizeof(PROCESSENTRY32); - for (fOk = Process32First( snap, ppe ); fOk; fOk = Process32Next( snap, ppe )) - if (ppe->th32ProcessID == id) - break; - - return fOk; -} - - -// Obtain the process and thread identifiers of the parent process. -BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name ) -{ - HANDLE hSnap; - PROCESSENTRY32 pe; - THREADENTRY32 te; - BOOL fOk; - - hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, 0 ); - - if (hSnap == INVALID_HANDLE_VALUE) - return FALSE; - - find_proc_id( hSnap, GetCurrentProcessId(), &pe ); - if (!find_proc_id( hSnap, pe.th32ParentProcessID, &pe )) - { - CloseHandle( hSnap ); - return FALSE; - } - - te.dwSize = sizeof(te); - for (fOk = Thread32First( hSnap, &te ); fOk; fOk = Thread32Next( hSnap, &te )) - if (te.th32OwnerProcessID == pe.th32ProcessID) - break; - - CloseHandle( hSnap ); - - ppi->dwProcessId = pe.th32ProcessID; - ppi->dwThreadId = te.th32ThreadID; - wcscpy( name, pe.szExeFile ); - - return fOk; -} - - -// Return the first non-space character from arg. -LPTSTR skip_spaces( LPTSTR arg ) -{ - while (*arg == ' ' || *arg == '\t') - ++arg; - - return arg; -} - - -// Retrieve an argument from the command line. cmd gets the existing argv; argv -// is ready for the next argument. -void get_arg( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd ) -{ - LPTSTR line; - - line = *cmd = skip_spaces( *argv ); - while (*line != '\0') - { - if (*line == ' ' || *line == '\t') - { - ++line; - break; - } - if (*line == '"') - { - while (*++line != '\0') - { - if (*line == '"') - { - ++line; - break; - } - *arg++ = *line; - } - } - else - *arg++ = *line++; - } - *arg = '\0'; - *argv = line; -} - - -void help( void ) -{ - _putws( -L"ANSICON by Jason Hood .\n" -L"Version " PVERS L" (" PDATE L"). Freeware.\n" -L"http://ansicon.adoxa.cjb.net/\n" -L"\n" -#ifdef _WIN64 -L"Process ANSI escape sequences in Windows console programs.\n" -#else -L"Process ANSI escape sequences in Win32 console programs.\n" -#endif -L"\n" -L"ansicon [-l] [-i] [-I] [-u] [-U] [-m[]] [-p]\n" -L" [-e|E string | -t|T [file(s)] | program [args]]\n" -L"\n" -L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n" -L" \t\t +4=output, +8=append) for program (-p is unaffected)\n" -L" -i\t\tinstall - add ANSICON to the AutoRun entry (also implies -p)\n" -L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n" -L" -I -U\t\tuse local machine instead of current user\n" -L" -m\t\tuse grey on black (\"monochrome\") or as default color\n" -L" -p\t\thook into the parent process\n" -L" -e\t\techo string\n" -L" -E\t\techo string, don't append newline\n" -L" -t\t\tdisplay files (\"-\" for stdin), combined as a single stream\n" -L" -T\t\tdisplay files, name first, blank line before and after\n" -L" program\trun the specified program\n" -L" nothing\trun a new command processor, or display stdin if redirected\n" -L"\n" -L" is one or two hexadecimal digits; please use \"COLOR /?\" for details.\n" -L"It may start with '-' to reverse foreground and background (but not for -p)." - ); -} +/* + ANSICON.c - ANSI escape sequence console driver. + + Jason Hood, 21 to 23 October, 2005. + + Original injection code was derived from Console Manager by Sergey Oblomov + (hoopoepg). Use of FlushInstructionCache came from www.catch22.net. + Additional information came from "Process-wide API spying - an ultimate hack", + Anton Bassov's article in "The Code Project" (use of OpenThread). + + v1.01, 11 & 12 March, 2006: + -m option to set "monochrome" (grey on black); + restore original color on exit. + + v1.10, 22 February, 2009: + ignore Ctrl+C/Ctrl+Break. + + v1.13, 21 & 27 March, 2009: + alternate injection method, to work with DEP; + use Unicode. + + v1.20, 17 to 21 June, 2009: + use a combination of the two injection methods; + test if ANSICON is already installed; + added -e (and -E) option to echo the command line (without newline); + added -t (and -T) option to type (display) files (with file name). + + v1.21, 23 September, 2009: + added -i (and -u) to add (remove) ANSICON to AutoRun. + + v1.24, 6 & 7 January, 2010: + no arguments to -t, or using "-" for the name, will read from stdin; + fix -t and -e when ANSICON was already loaded. + + v1.25, 22 July, 2010: + added -IU for HKLM. + + v1.30, 3 August to 7 September, 2010: + x64 support. + + v1.31, 13 & 15 November, 2010: + use LLW to fix potential Unicode path problems; + VC compatibility (2008 Express for 32-bit, PSDK 2003 R2 for 64-bit); + explicitly use wide characters (stick with TCHAR, but not ). + + v1.32, 4 to 22 December, 2010: + make -p more robust; + inject into GUI processes; + -i implies -p. + + v1.50, 7 to 14 December, 2011: + -u does not imply -p; + add the PID to the debugging output; + use ANSICON_VER to test if already installed; + always place first in AutoRun; + logging is always available, controlled by ANSICON_LOG environment variable; + only restore the original color after program/echo/type; + return program's exit code. + + 7 January, 2012: + fixed installing into a piped CMD.EXE; + added a log message indicating all imports have been processed. + + v1.52, 10 April, 2012: + fixed running "cmd" if "ComSpec" is not defined; + pass process & thread identifiers on the command line (for x86->x64). + + v1.60, 22 & 24 November, 2012: + set the code page to convert strings correctly; + expand wildcards for -t; + write the date if appending to the log. + + v1.62, 18 July, 2013: + write the bits to the log; + test if creating the registry key fails (HKLM requires admin privileges). + + v1.63, 25 July, 2013: + don't write the reset sequence if output is redirected. +*/ + +#define PDATE L"21 September, 2013" + +#include "ansicon.h" +#include "version.h" +#include +#include +#include +#include + +#ifdef __MINGW32__ +int _CRT_glob = 0; +#endif + + +#define CMDKEY L"Software\\Microsoft\\Command Processor" +#define AUTORUN L"AutoRun" + + +void help( void ); + +void display( LPCTSTR, BOOL ); +void print_error( LPCTSTR, ... ); +LPTSTR skip_spaces( LPTSTR ); +void get_arg( LPTSTR, LPTSTR*, LPTSTR* ); +void get_file( LPTSTR, LPTSTR*, LPTSTR* ); + +void process_autorun( TCHAR ); + +BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ); +BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR ); + + +// The DLL shares this variable, so injection requires it here. +#ifdef _WIN64 +DWORD LLW32; +extern LPVOID base; +#endif + + +// Find the name of the DLL and inject it. +BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app ) +{ + DWORD len; + WCHAR dll[MAX_PATH]; + int type; + + DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); + type = ProcessType( ppi, gui ); + if (type == 0) + { + fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app ); + return FALSE; + } + + len = (DWORD)(prog - prog_path); + memcpy( dll, prog_path, TSIZE(len) ); +#ifdef _WIN64 + wsprintf( dll + len, L"ANSI%d.dll", type ); + if (type == 32) + InjectDLL32( ppi, dll ); + else + InjectDLL64( ppi, dll ); +#else + wcscpy( dll + len, L"ANSI32.dll" ); + InjectDLL32( ppi, dll ); +#endif + return TRUE; +} + + +static HANDLE hConOut; +static CONSOLE_SCREEN_BUFFER_INFO csbi; + +void get_original_attr( void ) +{ + hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + GetConsoleScreenBufferInfo( hConOut, &csbi ); +} + + +void set_original_attr( void ) +{ + SetConsoleTextAttribute( hConOut, csbi.wAttributes ); + CloseHandle( hConOut ); +} + + +DWORD CtrlHandler( DWORD event ) +{ + return (event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT); +} + + +int main( void ) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + LPTSTR argv, arg, cmd; + TCHAR logstr[4]; + BOOL installed; + BOOL shell, run, gui; + HMODULE ansi; + DWORD len; + int rc = 0; + + argv = GetCommandLine(); + len = (DWORD)wcslen( argv ) + 1; + if (len < MAX_PATH) + len = MAX_PATH; + arg = malloc( TSIZE(len) ); + get_arg( arg, &argv, &cmd ); // skip the program name + get_arg( arg, &argv, &cmd ); + + if (*arg) + { + if (wcscmp( arg, L"/?" ) == 0 || + wcscmp( arg, L"--help" ) == 0) + { + help(); + return rc; + } + if (wcscmp( arg, L"--version" ) == 0) + { + _putws( L"ANSICON (" BITS L"-bit) version " PVERS L" (" PDATE L")." ); + return rc; + } + } + + prog = get_program_name( NULL ); + *logstr = '\0'; + GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); + log_level = _wtoi( logstr ); + + // Using "" for setlocale uses the system ANSI code page. + sprintf( (LPSTR)logstr, ".%u", GetConsoleOutputCP() ); + setlocale( LC_CTYPE, (LPSTR)logstr ); + +#ifdef _WIN64 + if (*arg == '-' && arg[1] == 'P') + { + swscanf( arg + 2, L"%u:%u", &pi.dwProcessId, &pi.dwThreadId ); + pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); + pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); + Inject( &pi, &gui, arg ); + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + return 0; + } +#endif + + if (log_level) + DEBUGSTR( 1, NULL ); // start a new session + + installed = (GetEnvironmentVariable( L"ANSICON_VER", NULL, 0 ) != 0); + // If it's already installed, remove it. This serves two purposes: preserves + // the parent's GRM; and unconditionally injects into GUI, without having to + // worry about ANSICON_GUI. + if (installed) + { + if (_isatty( 1 )) + fputws( L"\33[m", stdout ); + FreeLibrary( GetModuleHandle( ANSIDLL ) ); + } + + shell = run = TRUE; + get_original_attr(); + + while (*arg == '-') + { + switch (arg[1]) + { + case 'l': + SetEnvironmentVariable( L"ANSICON_LOG", arg + 2 ); + log_level = _wtoi( arg + 2 ); + DEBUGSTR( 1, NULL ); // create a session + break; + + case 'i': + case 'I': + case 'u': + case 'U': + shell = FALSE; + process_autorun( arg[1] ); + if (arg[1] == 'u' || arg[1] == 'U') + break; + // else fall through + + case 'p': + shell = FALSE; + // If it's already installed, there's no need to do anything. + if (installed) + { + DEBUGSTR( 1, L"Already installed" ); + } + else if (GetParentProcessInfo( &pi, arg )) + { + pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); + pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); + SuspendThread( pi.hThread ); +#ifdef _WIN64 + // Find the base address of kernel32.dll if the 64-bit version is + // injecting into a 32-bit parent. + if (IsWow64Process( pi.hProcess, &gui ) && gui) + { + HANDLE hSnap; + MODULEENTRY32 me; + BOOL fOk; + + hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE | + TH32CS_SNAPMODULE32, + pi.dwProcessId ); + if (hSnap != INVALID_HANDLE_VALUE) + { + me.dwSize = sizeof(MODULEENTRY32); + for (fOk = Module32First( hSnap, &me ); fOk; + fOk = Module32Next( hSnap, &me )) + { + if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0) + { + base = me.modBaseAddr; + break; + } + } + CloseHandle( hSnap ); + } + } +#endif + if (!Inject( &pi, &gui, arg )) + rc = 1; + ResumeThread( pi.hThread ); + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + } + else + { + fputws( L"ANSICON: could not obtain the parent process.\n", stderr ); + rc = 1; + } + break; + + case 'm': + { + int a = wcstol( arg + 2, NULL, 16 ); + if (a == 0) + a = (arg[2] == '-') ? -7 : 7; + if (a < 0) + { + SetEnvironmentVariable( L"ANSICON_REVERSE", L"1" ); + a = -a; + a = ((a >> 4) & 15) | ((a & 15) << 4); + } + SetConsoleTextAttribute( hConOut, (WORD)a ); + SetEnvironmentVariable( L"ANSICON_DEF", NULL ); + break; + } + + case 'e': + case 'E': + case 't': + case 'T': + run = FALSE; + ++arg; + goto arg_out; + } + get_arg( arg, &argv, &cmd ); + } +arg_out: + if (run && *cmd == '\0') + { + if (!shell) + run = FALSE; + else if (!_isatty( 0 )) + { + *arg = 't'; + run = FALSE; + } + } + + if (run) + { + if (*cmd == '\0') + { + if (GetEnvironmentVariable( L"ComSpec", arg, MAX_PATH )) + cmd = arg; + else + { + // CreateProcessW writes to the string, so can't simply point to "cmd". + static TCHAR cmdstr[] = L"cmd"; + cmd = cmdstr; + } + } + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi )) + { + Inject( &pi, &gui, arg ); + ResumeThread( pi.hThread ); + if (!gui) + { + SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); + WaitForSingleObject( pi.hProcess, INFINITE ); + GetExitCodeProcess( pi.hProcess, (LPDWORD)(LPVOID)&rc ); + } + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } + else + { + print_error( arg, arg ); + rc = 1; + } + } + else if (*arg) + { + ansi = LoadLibrary( ANSIDLL ); + if (ansi == NULL) + { + print_error( ANSIDLL ); + rc = 1; + } + + if (*arg == 'e' || *arg == 'E') + { + cmd += 2; + if (*cmd == ' ' || *cmd == '\t') + ++cmd; + fputws( cmd, stdout ); + if (*arg == 'e') + putwchar( '\n' ); + } + else // (*arg == 't' || *arg == 'T') + { + BOOL title = (*arg == 'T'); + get_file( arg, &argv, &cmd ); + if (*arg == '\0') + wcscpy( arg, L"-" ); + do + { + if (title) + { + wprintf( L"==> %s <==\n", arg ); + display( arg, title ); + putwchar( '\n' ); + } + else + display( arg, title ); + get_file( arg, &argv, &cmd ); + } while (*arg); + } + + FreeLibrary( ansi ); + } + + if (run || *arg) + set_original_attr(); + else + CloseHandle( hConOut ); + + return rc; +} + + +// Display a file. +void display( LPCTSTR name, BOOL title ) +{ + HANDLE in, out; + BOOL pipe; + char buf[8192]; + DWORD len; + + if (*name == '-' && name[1] == '\0') + { + pipe = TRUE; + in = GetStdHandle( STD_INPUT_HANDLE ); + } + else + { + pipe = FALSE; + in = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL ); + if (in == INVALID_HANDLE_VALUE) + { + print_error( name ); + return; + } + } + if (title) + { + putwchar( '\n' ); + // Need to flush, otherwise it's written *after* STD_OUTPUT_HANDLE should + // it be redirected. + fflush( stdout ); + } + out = GetStdHandle( STD_OUTPUT_HANDLE ); + for (;;) + { + if (!ReadFile( in, buf, sizeof(buf), &len, NULL )) + { + if (GetLastError() != ERROR_BROKEN_PIPE) + print_error( name ); + break; + } + if (len == 0) + break; + WriteFile( out, buf, len, &len, NULL ); + } + if (!pipe) + CloseHandle( in ); +} + + +void print_error( LPCTSTR name, ... ) +{ + LPTSTR errmsg = NULL; + DWORD err = GetLastError(); + va_list arg; + + if (err == ERROR_BAD_EXE_FORMAT) + { + // This error requires an argument, which is a duplicate of name. + va_start( arg, name ); + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, &arg ); + va_end( arg ); + fwprintf( stderr, L"ANSICON: %s", errmsg ); + } + else + { + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ); + // Just in case there are other messages requiring args... + if (errmsg == NULL) + fwprintf( stderr, L"ANSICON: %s: Error %lu.\n", name, err ); + else + fwprintf( stderr, L"ANSICON: %s: %s", name, errmsg ); + } + LocalFree( errmsg ); +} + + +// Add or remove ANSICON to AutoRun. +void process_autorun( TCHAR cmd ) +{ + HKEY cmdkey; + TCHAR ansicon[MAX_PATH+80]; + TCHAR logstr[80]; + LPTSTR autorun, ansirun; + DWORD len, type, exist; + BOOL inst; + + if (log_level) + _snwprintf( logstr, lenof(logstr), L"set ANSICON_LOG=%d&", log_level ); + else + *logstr = '\0'; + len = TSIZE(_snwprintf( ansicon, lenof(ansicon), + L"(if %%ANSICON_VER%%==^%%ANSICON_VER^%% %s\"%s\" -p)", + logstr, prog_path ) + 1); + + inst = (towlower( cmd ) == 'i'); + if (RegCreateKeyEx( (iswlower(cmd)) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, + CMDKEY, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, + &cmdkey, &exist ) != ERROR_SUCCESS) + { + fputws( L"ANSICON: could not update AutoRun", stderr ); + if (iswupper( cmd )) + fwprintf( stderr, L" (perhaps use -%c, or run as admin)", towlower(cmd) ); + fputws( L".\n", stderr ); + } + exist = 0; + RegQueryValueEx( cmdkey, AUTORUN, NULL, NULL, NULL, &exist ); + if (exist == 0) + { + if (inst) + RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); + } + else + { + // Let's assume there's sufficient memory. + autorun = malloc( exist + len ); + RegQueryValueEx( cmdkey, AUTORUN, NULL, &type, (PBYTE)autorun, &exist ); + // Remove the existing command, if present. + ansirun = wcsstr( autorun, L"(if %ANSICON_VER%" ); + if (ansirun != NULL) + { + LPTSTR tmp = wcschr( ansirun, '"' ); // opening quote + tmp = wcschr( tmp + 1, '"' ); // closing quote + tmp = wcschr( tmp + 1, ')' ); // closing bracket + if (*++tmp == '&') + ++tmp; + if (*tmp == '&') + ++tmp; + if (*tmp == '\0') + { + if (ansirun > autorun && ansirun[-1] == '&') + --ansirun; + if (ansirun > autorun && ansirun[-1] == '&') + --ansirun; + } + wcscpy( ansirun, tmp ); + exist = TSIZE((DWORD)wcslen( autorun ) + 1); + } + if (inst) + { + if (exist == sizeof(TCHAR)) + RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); + else + { + memmove( (PBYTE)autorun + len, autorun, exist ); + memcpy( autorun, ansicon, len ); + ((PBYTE)autorun)[len-sizeof(TCHAR)] = '&'; + RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist+len ); + } + } + else + { + if (exist == sizeof(TCHAR)) + RegDeleteValue( cmdkey, AUTORUN ); + else + RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist ); + } + free( autorun ); + } + RegCloseKey( cmdkey ); +} + + +// Search each process in the snapshot for id. +BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ) +{ + BOOL fOk; + + ppe->dwSize = sizeof(PROCESSENTRY32); + for (fOk = Process32First( snap, ppe ); fOk; fOk = Process32Next( snap, ppe )) + if (ppe->th32ProcessID == id) + break; + + return fOk; +} + + +// Obtain the process and thread identifiers of the parent process. +BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name ) +{ + HANDLE hSnap; + PROCESSENTRY32 pe; + THREADENTRY32 te; + BOOL fOk; + + hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, 0 ); + + if (hSnap == INVALID_HANDLE_VALUE) + return FALSE; + + find_proc_id( hSnap, GetCurrentProcessId(), &pe ); + if (!find_proc_id( hSnap, pe.th32ParentProcessID, &pe )) + { + CloseHandle( hSnap ); + return FALSE; + } + + te.dwSize = sizeof(te); + for (fOk = Thread32First( hSnap, &te ); fOk; fOk = Thread32Next( hSnap, &te )) + if (te.th32OwnerProcessID == pe.th32ProcessID) + break; + + CloseHandle( hSnap ); + + ppi->dwProcessId = pe.th32ProcessID; + ppi->dwThreadId = te.th32ThreadID; + wcscpy( name, pe.szExeFile ); + + return fOk; +} + + +// Return the first non-space character from arg. +LPTSTR skip_spaces( LPTSTR arg ) +{ + while (*arg == ' ' || *arg == '\t') + ++arg; + + return arg; +} + + +// Retrieve an argument from the command line. cmd gets the existing argv; argv +// is ready for the next argument. +void get_arg( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd ) +{ + LPTSTR line; + + line = *cmd = skip_spaces( *argv ); + while (*line != '\0') + { + if (*line == ' ' || *line == '\t') + { + ++line; + break; + } + if (*line == '"') + { + while (*++line != '\0') + { + if (*line == '"') + { + ++line; + break; + } + *arg++ = *line; + } + } + else + *arg++ = *line++; + } + *arg = '\0'; + *argv = line; +} + + +int glob_sort( const void* a, const void* b ) +{ + return lstrcmpi( *(LPCTSTR*)a, *(LPCTSTR*)b ); +} + + +// As get_arg, but expand wildcards. +void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd ) +{ + HANDLE fh, in; + WIN32_FIND_DATA fd; + LPTSTR path; + int size; + char buf[1024]; + static LPTSTR name; + static LPTSTR* glob; + static int globbed; + + if (globbed != 0) + { + if (glob[globbed] == NULL) + { + free( glob ); + globbed = 0; + } + else + { + wcscpy( name, glob[globbed++] ); + return; + } + } + + get_arg( arg, argv, cmd ); + if (wcspbrk( arg, L"*?" ) != NULL) + { + fh = FindFirstFile( arg, &fd ); + if (fh != INVALID_HANDLE_VALUE) + { + size = 0; + do + { + if (! (fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | + FILE_ATTRIBUTE_HIDDEN))) + { + ++globbed; + size += (int)wcslen( fd.cFileName ) + 1; + } + } while (FindNextFile( fh, &fd )); + FindClose( fh ); + + if (globbed != 0) + { + for (path = name = arg; *path != '\0'; ++path) + if (*path == '\\' || *path == '/') + name = path + 1; + glob = malloc( (globbed + 1) * sizeof(LPTSTR) + TSIZE(size) ); + path = (LPTSTR)(glob + globbed + 1); + globbed = 0; + fh = FindFirstFile( arg, &fd ); + do + { + if (! (fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | + FILE_ATTRIBUTE_HIDDEN))) + { + // Ignore apparent binary files. + wcscpy( name, fd.cFileName ); + in = CreateFile( arg, GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL ); + if (in != INVALID_HANDLE_VALUE) + { + ReadFile( in, buf, sizeof(buf), (LPVOID)&size, NULL ); + CloseHandle( in ); + if (memchr( buf, 0, size ) != NULL) + continue; + } + size = (int)wcslen( fd.cFileName ) + 1; + memcpy( path, fd.cFileName, TSIZE(size) ); + glob[globbed++] = path; + path += size; + } + } while (FindNextFile( fh, &fd )); + FindClose( fh ); + glob[globbed] = NULL; + + qsort( glob, globbed, sizeof(LPTSTR), glob_sort ); + + wcscpy( name, glob[0] ); + globbed = 1; + } + } + } +} + + +void help( void ) +{ + _putws( +L"ANSICON by Jason Hood .\n" +L"Version " PVERS L" (" PDATE L"). Freeware.\n" +L"http://ansicon.adoxa.vze.com/\n" +L"\n" +#ifdef _WIN64 +L"Process ANSI escape sequences in Windows console programs.\n" +#else +L"Process ANSI escape sequences in Win32 console programs.\n" +#endif +L"\n" +L"ansicon [-l] [-i] [-I] [-u] [-U] [-m[]] [-p]\n" +L" [-e|E string | -t|T [file(s)] | program [args]]\n" +L"\n" +L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n" +L" \t\t +4=output, +8=append) for program (-p is unaffected)\n" +L" -i\t\tinstall - add ANSICON to the AutoRun entry (also implies -p)\n" +L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n" +L" -I -U\t\tuse local machine instead of current user\n" +L" -m\t\tuse grey on black (\"monochrome\") or as default color\n" +L" -p\t\thook into the parent process\n" +L" -e\t\techo string\n" +L" -E\t\techo string, don't append newline\n" +L" -t\t\tdisplay files (\"-\" for stdin), combined as a single stream\n" +L" -T\t\tdisplay files, name first, blank line before and after\n" +L" program\trun the specified program\n" +L" nothing\trun a new command processor, or display stdin if redirected\n" +L"\n" +L" is one or two hexadecimal digits; please use \"COLOR /?\" for details.\n" +L"It may start with '-' to reverse foreground and background (but not for -p)." + ); +} diff --git a/ansicon/ansicon.h b/ansicon/ansicon.h old mode 100755 new mode 100644 index 8819186..f5cc47a --- a/ansicon/ansicon.h +++ b/ansicon/ansicon.h @@ -1,47 +1,51 @@ -/* - ansicon.h - Header file for common definitions. - - Jason Hood, 12 December, 2010 (originally injdll.h, 20 June, 2009). -*/ - -#ifndef ANSICON_H -#define ANSICON_H - -#ifndef UNICODE -# define UNICODE -#endif - -#define WIN32_LEAN_AND_MEAN -#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread -#include -#include -#include - -#define lenof(array) (sizeof(array)/sizeof(*(array))) -#define TSIZE(size) ((size) * sizeof(TCHAR)) - - -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, BOOL* ); -void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR ); -void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR ); - -extern TCHAR prog_path[MAX_PATH]; -extern LPTSTR prog; -LPTSTR get_program_name( LPTSTR ); - -extern int log_level; -void DEBUGSTR( int level, LPTSTR szFormat, ... ); - -#endif +/* + ansicon.h - Header file for common definitions. + + Jason Hood, 12 December, 2010 (originally injdll.h, 20 June, 2009). +*/ + +#ifndef ANSICON_H +#define ANSICON_H + +#ifndef UNICODE +# define UNICODE +#endif + +#define WIN32_LEAN_AND_MEAN +#ifdef _WIN64 +#define _WIN32_WINNT 0x0600 // MinGW-w64 wants this defined for Wow64 stuff +#else +#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread +#endif +#include +#include +#include + +#define lenof(array) (sizeof(array)/sizeof(*(array))) +#define TSIZE(size) ((size) * sizeof(TCHAR)) + + +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, BOOL* ); +void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR ); +void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR ); + +extern TCHAR prog_path[MAX_PATH]; +extern LPTSTR prog; +LPTSTR get_program_name( LPTSTR ); + +extern int log_level; +void DEBUGSTR( int level, LPTSTR szFormat, ... ); + +#endif diff --git a/ansicon/ansicon.rc b/ansicon/ansicon.rc old mode 100755 new mode 100644 index 09b03c1..908044f --- a/ansicon/ansicon.rc +++ b/ansicon/ansicon.rc @@ -1,36 +1,54 @@ -/* - ansicon.rc - Version resource for ansicon.exe. - - Jason Hood, 11 November, 2009. -*/ - -#include -#include "version.h" - -1 VERSIONINFO -FILEVERSION PVERB -PRODUCTVERSION PVERB -FILEOS VOS_NT -FILETYPE VFT_APP -{ - BLOCK "StringFileInfo" - { - BLOCK "040904B0" - { - VALUE "Comments", "http://ansicon.adoxa.cjb.net/" - VALUE "CompanyName", "Jason Hood" - VALUE "FileDescription", "ANSI Console" - VALUE "FileVersion", PVERSA - VALUE "InternalName", "ansicon" - VALUE "LegalCopyright", "Freeware" - VALUE "OriginalFilename", "ansicon.exe" - VALUE "ProductName", "ANSICON" - VALUE "ProductVersion", PVERSA - } - } - - BLOCK "VarFileInfo" - { - VALUE "Translation", 0x0409, 0x04B0 - } -} +/* + ansicon.rc - Version resource for ansicon.exe. + + Jason Hood, 11 November, 2009. +*/ + +#include +#include "version.h" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION PVERB +PRODUCTVERSION PVERB +FILEOS VOS_NT +FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" + { + BLOCK "040904B0" + { + VALUE "Comments", "http://ansicon.adoxa.vze.com/" + VALUE "CompanyName", "Jason Hood" + VALUE "FileDescription", "ANSI Console" + VALUE "FileVersion", PVERSA + VALUE "InternalName", "ansicon" + VALUE "LegalCopyright", "Freeware" + VALUE "OriginalFilename", "ansicon.exe" + VALUE "ProductName", "ANSICON" + VALUE "ProductVersion", PVERSA + } + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04B0 + } +} + +// Add a manifest for the 32-bit version, to prevent registry redirection when +// trying to use HKLM. +#ifndef _WIN64 +1 24 +{ +"\ +\ +\ + \ + \ + \ + \ + \ +\ +" +} +#endif diff --git a/ansicon/injdll32.c b/ansicon/injdll32.c old mode 100755 new mode 100644 index 25ed7ae..1a299de --- a/ansicon/injdll32.c +++ b/ansicon/injdll32.c @@ -1,123 +1,260 @@ -/* - Inject code into the target process to load our DLL. The target thread - should be suspended on entry; it remains suspended on exit. - - Initially I used the "stack" method of injection. However, this fails - when DEP is active, since that doesn't allow code to execute in the stack. - To overcome this I used the "CreateRemoteThread" method. However, this - would fail with Wselect, a program to assist batch files. Wselect runs, - but it has no output. As it turns out, removing the suspended flag would - make Wselect work, but it caused problems with everything else. So now I - allocate a section of memory and change the context to run from there. At - first I had an event to signal when the library was loaded, then the memory - was released. However, that wouldn't work with -p and CMD.EXE (4NT v8 - worked fine). Since it's possible the DLL might start a process suspended, - I've decided to simply keep the memory. -*/ - -#include "ansicon.h" - -#ifdef _WIN64 -#if defined(__MINGW64__) || (defined(_MSC_VER) && _MSC_VER <= 1400) -#include "wow64.h" - -TWow64GetThreadContext Wow64GetThreadContext; -TWow64SetThreadContext Wow64SetThreadContext; -#endif - -#define CONTEXT WOW64_CONTEXT -#undef CONTEXT_CONTROL -#define CONTEXT_CONTROL WOW64_CONTEXT_CONTROL -#define GetThreadContext Wow64GetThreadContext -#define SetThreadContext Wow64SetThreadContext -#endif - - -DWORD LLW; - - -void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) -{ - CONTEXT context; - DWORD len; - LPVOID mem; - DWORD mem32; - #define CODESIZE 20 - BYTE code[CODESIZE+TSIZE(MAX_PATH)]; - union - { - PBYTE pB; - PDWORD pL; - } ip; - - len = TSIZE(lstrlen( dll ) + 1); - if (len > TSIZE(MAX_PATH)) - return; - - if (LLW == 0) - { - HMODULE hKernel = GetModuleHandleA( "kernel32.dll" ); -#ifdef _WIN64 -#ifdef __MINGW64__ - #define GETPROC( proc ) proc = (T##proc)GetProcAddress( hKernel, #proc ) - GETPROC( Wow64GetThreadContext ); - GETPROC( Wow64SetThreadContext ); - // Assume if one is defined, so is the other. - if (Wow64GetThreadContext == 0) - { - DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext.\n" ); - return; - } -#endif - - STARTUPINFO si; - PROCESS_INFORMATION pi; - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - // ...ANSI32.dll\0 - CopyMemory( code, dll, len - TSIZE(7) ); - // ...ANSI-LLW.exe\0 - CopyMemory( code + len - TSIZE(7), L"-LLW.exe", TSIZE(9) ); - if (!CreateProcess( (LPCTSTR)code, NULL, NULL, NULL, FALSE, 0, NULL, NULL, - &si, &pi )) - { - DEBUGSTR( 1, L"Failed to execute \"%s\".\n", (LPCTSTR)code ); - return; - } - WaitForSingleObject( pi.hProcess, INFINITE ); - GetExitCodeProcess( pi.hProcess, &LLW ); - CloseHandle( pi.hProcess ); - CloseHandle( pi.hThread ); -#else - LLW = (DWORD)GetProcAddress( hKernel, "LoadLibraryW" ); -#endif - } - - CopyMemory( code + CODESIZE, dll, len ); - len += CODESIZE; - - context.ContextFlags = CONTEXT_CONTROL; - GetThreadContext( ppi->hThread, &context ); - mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, - PAGE_EXECUTE_READWRITE ); - mem32 = (DWORD)(DWORD_PTR)mem; - - ip.pB = code; - - *ip.pB++ = 0x68; // push eip - *ip.pL++ = context.Eip; - *ip.pB++ = 0x9c; // pushf - *ip.pB++ = 0x60; // pusha - *ip.pB++ = 0x68; // push L"path\to\ANSI32.dll" - *ip.pL++ = mem32 + CODESIZE; - *ip.pB++ = 0xe8; // call LoadLibraryW - *ip.pL++ = LLW - (mem32 + (DWORD)(ip.pB+4 - code)); - *ip.pB++ = 0x61; // popa - *ip.pB++ = 0x9d; // popf - *ip.pB++ = 0xc3; // ret - - WriteProcessMemory( ppi->hProcess, mem, code, len, NULL ); - FlushInstructionCache( ppi->hProcess, mem, len ); - context.Eip = mem32; - SetThreadContext( ppi->hThread, &context ); -} +/* + Inject code into the target process to load our DLL. The target thread + should be suspended on entry; it remains suspended on exit. + + Initially I used the "stack" method of injection. However, this fails + when DEP is active, since that doesn't allow code to execute in the stack. + To overcome this I used the "CreateRemoteThread" method. However, this + would fail with Wselect, a program to assist batch files. Wselect runs, + but it has no output. As it turns out, removing the suspended flag would + make Wselect work, but it caused problems with everything else. So now I + allocate a section of memory and change the context to run from there. At + first I had an event to signal when the library was loaded, then the memory + was released. However, that wouldn't work with -p and CMD.EXE (4NT v8 + worked fine). Since it's possible the DLL might start a process suspended, + I've decided to simply keep the memory. +*/ + +#include "ansicon.h" + +#ifdef _WIN64 +#ifndef WOW64_CONTEXT_ALL +#include "wow64.h" + +TWow64GetThreadContext Wow64GetThreadContext; +TWow64SetThreadContext Wow64SetThreadContext; +#define IMPORT_WOW64 +#endif + +#define CONTEXT WOW64_CONTEXT +#undef CONTEXT_CONTROL +#define CONTEXT_CONTROL WOW64_CONTEXT_CONTROL +#define GetThreadContext Wow64GetThreadContext +#define SetThreadContext Wow64SetThreadContext + +#define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset)) + +extern DWORD LLW32; +LPVOID base; +static PIMAGE_DOS_HEADER pDosHeader; + +int export_cmp( const void* a, const void* b ) +{ + return strcmp( (LPCSTR)a, MakeVA( LPCSTR, *(const PDWORD)b ) ); +} + + +/* + Get the relative address of the 32-bit LoadLibraryW function from 64-bit code. + This was originally done via executing a helper program (ANSI-LLW.exe), but I + never liked doing that, so now I do it the "hard" way - load the 32-bit + kernel32.dll directly and search the exports. +*/ +BOOL get_LLW32( void ) +{ + HMODULE kernel32; + TCHAR buf[MAX_PATH]; + UINT len; + PIMAGE_NT_HEADERS32 pNTHeader; + PIMAGE_EXPORT_DIRECTORY pExportDir; + PDWORD fun_table, name_table; + PWORD ord_table; + PDWORD pLLW; + + len = GetSystemWow64Directory( buf, MAX_PATH ); + wcscpy( buf + len, L"\\kernel32.dll" ); + // MinGW-w64 had a typo, calling it LINRARY. + kernel32 = LoadLibraryEx( buf, NULL, 0x20/*LOAD_LIBRARY_AS_IMAGE_RESOURCE*/ ); + if (kernel32 == NULL) + { + DEBUGSTR( 1, L"Unable to load 32-bit kernel32.dll!" ); + return FALSE; + } + // The handle uses low bits as flags, so strip 'em off. + pDosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)kernel32 & ~0xFFFF); + pNTHeader = MakeVA( PIMAGE_NT_HEADERS32, pDosHeader->e_lfanew ); + pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, + pNTHeader->OptionalHeader. + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. + VirtualAddress ); + + fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions ); + name_table = MakeVA( PDWORD, pExportDir->AddressOfNames ); + ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals ); + + pLLW = bsearch( "LoadLibraryW", name_table, pExportDir->NumberOfNames, + sizeof(DWORD), export_cmp ); + if (pLLW == NULL) + { + DEBUGSTR( 1, L"Could not find LoadLibraryW!" ); + FreeLibrary( kernel32 ); + return FALSE; + } + LLW32 = fun_table[ord_table[pLLW - name_table]]; + + FreeLibrary( kernel32 ); + return TRUE; +} +#else +DWORD LLW32; +#endif + + +void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) +{ + CONTEXT context; + DWORD ep; + DWORD len; + LPVOID mem; + DWORD mem32; + DWORD pr; + #define CODESIZE 20 + BYTE code[CODESIZE+TSIZE(MAX_PATH)]; + union + { + PBYTE pB; + PDWORD pL; + } ip; +#ifdef _WIN64 + BOOL entry = FALSE; +#endif + +#ifdef IMPORT_WOW64 + if (Wow64GetThreadContext == 0) + { + #define GETPROC( proc ) proc = (T##proc)GetProcAddress( hKernel, #proc ) + HMODULE hKernel = GetModuleHandle( L"kernel32.dll" ); + GETPROC( Wow64GetThreadContext ); + GETPROC( Wow64SetThreadContext ); + // Assume if one is defined, so is the other. + if (Wow64GetThreadContext == 0) + { + DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext." ); + return; + } + } +#endif + + len = TSIZE(lstrlen( dll ) + 1); + if (len > TSIZE(MAX_PATH)) + return; + + CopyMemory( code + CODESIZE, dll, len ); + len += CODESIZE; + + context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + GetThreadContext( ppi->hThread, &context ); + mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, + PAGE_READWRITE ); + mem32 = (DWORD)(DWORD_PTR)mem; + + ip.pB = code; + + ep = context.Eip; + if (LLW32 == 0) + { +#ifndef _WIN64 + LLW32 = (DWORD)GetProcAddress( GetModuleHandle( L"kernel32.dll" ), + "LoadLibraryW" ); +#else + struct unicode_string + { + USHORT Length; + USHORT MaximumLength; + DWORD Buffer; + }; + struct ldr_module // incomplete definition + { + DWORD next, prev; + DWORD baseAddress; + DWORD entryPoint; + DWORD sizeOfImage; + struct unicode_string fullDllName; + struct unicode_string baseDllName; + } ldr; + WCHAR basename[MAX_PATH]; + + if (!get_LLW32()) + return; + // Determine the base address of the 32-bit kernel32.dll. If injecting + // into the parent process, base has already been determined. Otherwise, + // use the PEB to walk the loaded modules. + if (base == 0) + { + // When a process is created suspended, EAX has the entry point and EBX + // points to the PEB. + if (!ReadProcessMemory( ppi->hProcess, UIntToPtr( context.Ebx + 0x0C ), + ip.pL, 4, NULL )) + { + DEBUGSTR( 1, L"Failed to read Ldr from PEB." ); + return; + } + // In case we're a bit slow (which seems to be unlikely), set up an + // infinite loop as the entry point. + WriteProcessMemory( ppi->hProcess, mem, "\xEB\xFE", 2, NULL ); + FlushInstructionCache( ppi->hProcess, mem, 2 ); + ep = context.Eax; + context.Eax = mem32; + SetThreadContext( ppi->hThread, &context ); + VirtualProtectEx( ppi->hProcess, mem, len, PAGE_EXECUTE, &pr ); + // Now resume the thread, as the PEB hasn't even been created yet. + ResumeThread( ppi->hThread ); + while (*ip.pL == 0) + { + Sleep( 0 ); + ReadProcessMemory( ppi->hProcess, UIntToPtr( context.Ebx + 0x0C ), + ip.pL, 4, NULL ); + } + // Read PEB_LDR_DATA.InInitializationOrderModuleList.Flink. + ReadProcessMemory( ppi->hProcess, UIntToPtr( *ip.pL + 0x1c ), + &ip.pL[1], 4, NULL ); + // Sometimes we're so quick ntdll.dll is the only one present, so keep + // looping until kernel32.dll shows up. + for (;;) + { + ldr.next = ip.pL[1]; + do + { + ReadProcessMemory( ppi->hProcess, UIntToPtr( ldr.next ), + &ldr, sizeof(ldr), NULL ); + ReadProcessMemory( ppi->hProcess, UIntToPtr( ldr.baseDllName.Buffer ), + basename, ldr.baseDllName.MaximumLength, NULL ); + if (_wcsicmp( basename, L"kernel32.dll" ) == 0) + { + LLW32 += ldr.baseAddress; + goto gotit; + } + } while (ldr.next != *ip.pL + 0x1c); + } + gotit: + SuspendThread( ppi->hThread ); + VirtualProtectEx( ppi->hProcess, mem, len, pr, &pr ); + entry = TRUE; + } + else + LLW32 += PtrToUint( base ); +#endif + } + + *ip.pB++ = 0x68; // push ep + *ip.pL++ = ep; + *ip.pB++ = 0x9c; // pushf + *ip.pB++ = 0x60; // pusha + *ip.pB++ = 0x68; // push L"path\to\ANSI32.dll" + *ip.pL++ = mem32 + CODESIZE; + *ip.pB++ = 0xe8; // call LoadLibraryW + *ip.pL++ = LLW32 - (mem32 + (DWORD)(ip.pB+4 - code)); + *ip.pB++ = 0x61; // popa + *ip.pB++ = 0x9d; // popf + *ip.pB++ = 0xc3; // ret + + WriteProcessMemory( ppi->hProcess, mem, code, len, NULL ); + FlushInstructionCache( ppi->hProcess, mem, len ); + VirtualProtectEx( ppi->hProcess, mem, len, PAGE_EXECUTE, &pr ); +#ifdef _WIN64 + if (entry) + return; +#endif + context.Eip = mem32; + SetThreadContext( ppi->hThread, &context ); +} diff --git a/ansicon/injdll64.c b/ansicon/injdll64.c old mode 100755 new mode 100644 index c46bd46..9e1c65c --- a/ansicon/injdll64.c +++ b/ansicon/injdll64.c @@ -1,96 +1,96 @@ -/* - Inject code into the target process to load our DLL. The target thread - should be suspended on entry; it remains suspended on exit. - - Initially I used the "stack" method of injection. However, this fails - when DEP is active, since that doesn't allow code to execute in the stack. - To overcome this I used the "CreateRemoteThread" method. However, this - would fail with Wselect, a program to assist batch files. Wselect runs, - but it has no output. As it turns out, removing the suspended flag would - make Wselect work, but it caused problems with everything else. So now I - allocate a section of memory and change the context to run from there. At - first I had an event to signal when the library was loaded, then the memory - was released. However, that wouldn't work with -p and CMD.EXE (4NT v8 - worked fine). Since it's possible the DLL might start a process suspended, - I've decided to simply keep the memory. -*/ - -#include "ansicon.h" - -void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) -{ - CONTEXT context; - DWORD len; - LPVOID mem; - DWORD64 LLW; - union - { - PBYTE pB; - PDWORD64 pL; - } ip; - #define CODESIZE 92 - static BYTE code[CODESIZE+TSIZE(MAX_PATH)] = { - 0,0,0,0,0,0,0,0, // original rip - 0,0,0,0,0,0,0,0, // LoadLibraryW - 0x9C, // pushfq - 0x50, // push rax - 0x51, // push rcx - 0x52, // push rdx - 0x53, // push rbx - 0x55, // push rbp - 0x56, // push rsi - 0x57, // push rdi - 0x41,0x50, // push r8 - 0x41,0x51, // push r9 - 0x41,0x52, // push r10 - 0x41,0x53, // push r11 - 0x41,0x54, // push r12 - 0x41,0x55, // push r13 - 0x41,0x56, // push r14 - 0x41,0x57, // push r15 - 0x48,0x83,0xEC,0x28, // sub rsp, 40 - 0x48,0x8D,0x0D,41,0,0,0, // lea ecx, L"path\to\ANSI64.dll" - 0xFF,0x15,-49,-1,-1,-1, // call LoadLibraryW - 0x48,0x83,0xC4,0x28, // add rsp, 40 - 0x41,0x5F, // pop r15 - 0x41,0x5E, // pop r14 - 0x41,0x5D, // pop r13 - 0x41,0x5C, // pop r12 - 0x41,0x5B, // pop r11 - 0x41,0x5A, // pop r10 - 0x41,0x59, // pop r9 - 0x41,0x58, // pop r8 - 0x5F, // pop rdi - 0x5E, // pop rsi - 0x5D, // pop rbp - 0x5B, // pop rbx - 0x5A, // pop rdx - 0x59, // pop rcx - 0x58, // pop rax - 0x9D, // popfq - 0xFF,0x25,-91,-1,-1,-1, // jmp original Rip - 0, // dword alignment for LLW, fwiw - }; - - len = TSIZE(lstrlen( dll ) + 1); - if (len > TSIZE(MAX_PATH)) - return; - CopyMemory( code + CODESIZE, dll, len ); - len += CODESIZE; - - context.ContextFlags = CONTEXT_CONTROL; - GetThreadContext( ppi->hThread, &context ); - mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, - PAGE_EXECUTE_READWRITE ); - LLW = (DWORD64)LoadLibraryW; - - ip.pB = code; - - *ip.pL++ = context.Rip; - *ip.pL++ = LLW; - - WriteProcessMemory( ppi->hProcess, mem, code, len, NULL ); - FlushInstructionCache( ppi->hProcess, mem, len ); - context.Rip = (DWORD64)mem + 16; - SetThreadContext( ppi->hThread, &context ); -} +/* + Inject code into the target process to load our DLL. The target thread + should be suspended on entry; it remains suspended on exit. + + Initially I used the "stack" method of injection. However, this fails + when DEP is active, since that doesn't allow code to execute in the stack. + To overcome this I used the "CreateRemoteThread" method. However, this + would fail with Wselect, a program to assist batch files. Wselect runs, + but it has no output. As it turns out, removing the suspended flag would + make Wselect work, but it caused problems with everything else. So now I + allocate a section of memory and change the context to run from there. At + first I had an event to signal when the library was loaded, then the memory + was released. However, that wouldn't work with -p and CMD.EXE (4NT v8 + worked fine). Since it's possible the DLL might start a process suspended, + I've decided to simply keep the memory. +*/ + +#include "ansicon.h" + +void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) +{ + CONTEXT context; + DWORD len; + LPVOID mem; + DWORD64 LLW; + union + { + PBYTE pB; + PDWORD64 pL; + } ip; + #define CODESIZE 92 + static BYTE code[CODESIZE+TSIZE(MAX_PATH)] = { + 0,0,0,0,0,0,0,0, // original rip + 0,0,0,0,0,0,0,0, // LoadLibraryW + 0x9C, // pushfq + 0x50, // push rax + 0x51, // push rcx + 0x52, // push rdx + 0x53, // push rbx + 0x55, // push rbp + 0x56, // push rsi + 0x57, // push rdi + 0x41,0x50, // push r8 + 0x41,0x51, // push r9 + 0x41,0x52, // push r10 + 0x41,0x53, // push r11 + 0x41,0x54, // push r12 + 0x41,0x55, // push r13 + 0x41,0x56, // push r14 + 0x41,0x57, // push r15 + 0x48,0x83,0xEC,0x28, // sub rsp, 40 + 0x48,0x8D,0x0D,41,0,0,0, // lea ecx, L"path\to\ANSI64.dll" + 0xFF,0x15,-49,-1,-1,-1, // call LoadLibraryW + 0x48,0x83,0xC4,0x28, // add rsp, 40 + 0x41,0x5F, // pop r15 + 0x41,0x5E, // pop r14 + 0x41,0x5D, // pop r13 + 0x41,0x5C, // pop r12 + 0x41,0x5B, // pop r11 + 0x41,0x5A, // pop r10 + 0x41,0x59, // pop r9 + 0x41,0x58, // pop r8 + 0x5F, // pop rdi + 0x5E, // pop rsi + 0x5D, // pop rbp + 0x5B, // pop rbx + 0x5A, // pop rdx + 0x59, // pop rcx + 0x58, // pop rax + 0x9D, // popfq + 0xFF,0x25,-91,-1,-1,-1, // jmp original Rip + 0, // dword alignment for LLW, fwiw + }; + + len = TSIZE(lstrlen( dll ) + 1); + if (len > TSIZE(MAX_PATH)) + return; + CopyMemory( code + CODESIZE, dll, len ); + len += CODESIZE; + + context.ContextFlags = CONTEXT_CONTROL; + GetThreadContext( ppi->hThread, &context ); + mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, + PAGE_EXECUTE_READWRITE ); + LLW = (DWORD64)LoadLibraryW; + + ip.pB = code; + + *ip.pL++ = context.Rip; + *ip.pL++ = LLW; + + WriteProcessMemory( ppi->hProcess, mem, code, len, NULL ); + FlushInstructionCache( ppi->hProcess, mem, len ); + context.Rip = (DWORD64)mem + 16; + SetThreadContext( ppi->hThread, &context ); +} diff --git a/ansicon/makefile b/ansicon/makefile deleted file mode 100755 index 0007bee..0000000 --- a/ansicon/makefile +++ /dev/null @@ -1,82 +0,0 @@ -# Makefile for ANSICON. -# Jason Hood, 11 March, 2006. Updated 20 June, 2009. - -# I've used TDM64 (gcc 4.6.1), building the 32-bit version in the x86 directory -# and the 64-bit version in the x64 directory. MinGW32 (gcc 3.4.5) will also -# build the 32-bit version. - -# 19 November, 2010: -# explicitly use 64-bit flags, in case the compiler isn't. -# -# 13 December, 2011: -# use CMD for file operations, not programs from fileutils. - -CC = gcc -CFLAGS = -O2 -Wall - -X86OBJS = x86/proctype.o x86/injdll32.o x86/util.o -X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/util.o - -x86/%.o: %.c ansicon.h - $(CC) -m32 -c $(CFLAGS) $< -o $@ - -x86/%v.o: %.rc version.h - windres -U _WIN64 -F pe-i386 $< $@ - -x64/%.o: %.c ansicon.h - $(CC) -m64 -c $(CFLAGS) $< -o $@ - -x64/%v.o: %.rc version.h - windres -F pe-x86-64 $< $@ - -all: ansicon32 ansicon64 - -ansicon32: x86 x86/ansicon.exe x86/ANSI32.dll - -ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll x64/ANSI-LLW.exe - -x86: - cmd /c "mkdir x86" - -x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o - $(CC) -m32 $+ -s -o $@ - -x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o - $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared - -x64: - cmd /c "mkdir x64" - -x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o - $(CC) -m64 $+ -s -o $@ - -x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o - $(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared - -x64/ANSI32.dll: x64/ANSI32.o x64/proctype32.o x86/injdll32.o x86/util.o x86/ansiv.o - $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared - -x64/ANSI-LLW.exe: ANSI-LLW.c - $(CC) -m32 $(CFLAGS) $< -s -o $@ - -x86/ansicon.o: version.h -x86/ANSI.o: version.h -x64/ansicon.o: version.h -x64/ANSI.o: version.h -x86/util.o: version.h -x64/util.o: version.h -x86/ansiconv.o: ansicon.rc -x86/ansiv.o: ansi.rc -x64/ansiconv.o: ansicon.rc -x64/ansiv.o: ansi.rc - -x64/ANSI32.o: ANSI.c - $(CC) -m32 -DW32ON64 $(CFLAGS) $< -c -o $@ -x64/proctype32.o: proctype.c - $(CC) -m32 -DW32ON64 $(CFLAGS) $< -c -o $@ - -# Need two commands, because if the directory doesn't exist, it won't delete -# anything at all. -clean: - -cmd /c "del x86\*.o 2>nul" - -cmd /c "del x64\*.o 2>nul" diff --git a/ansicon/makefile.gcc b/ansicon/makefile.gcc new file mode 100644 index 0000000..75543ca --- /dev/null +++ b/ansicon/makefile.gcc @@ -0,0 +1,112 @@ +# MinGW/MinGW-w64 makefile for ANSICON. +# Jason Hood, 11 March, 2006. Updated 20 June, 2009. +# +# 19 November, 2010: +# explicitly use 64-bit flags, in case the compiler isn't. +# +# 13 December, 2011: +# use CMD for file operations, not programs from fileutils. +# +# 23 November, 2012: +# set the base address of the DLLs to AC0000[00] (AnsiCon). +# +# 17 & 18 July, 2013: +# work with 32-bit only, 64-bit only or multilib compilers; +# hide the commands (use V=1 to show them). +# +# Tested with: +# * MinGW/gcc 4.7.2; +# * tdm-gcc-4.7.1-2; +# * tdm64-gcc-4.7.1-3; +# * MinGW-builds x64-4.8.1-release-posix-seh-rev1. + +CC = gcc +CFLAGS = -O2 -Wall + +#ARCH = 32 +#ARCH = 64 +#ARCH = multi + +ifndef ARCH +# Use the machine to distinguish between MinGW and MinGW-w64. +ifeq (,$(findstring 64,$(shell gcc -dumpmachine))) +ARCH = 32 +else +# It's 64-bit, if it's multi the lib name will be different. +ifeq ($(shell gcc -m32 -print-libgcc-file-name),$(shell gcc -m64 -print-libgcc-file-name)) +ARCH = 64 +else +ARCH = multi +endif +endif +endif + +X86OBJS = x86/proctype.o x86/injdll32.o x86/util.o +X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/util.o + +V ?= 0 +ifeq ($(V),0) +CCmsg = @echo $<; +RCmsg = $(CCmsg) +LDmsg = @echo $@; +endif + +x86/%.o: %.c ansicon.h + $(CCmsg)$(CC) -m32 -c $(CFLAGS) $< -o $@ + +x86/%v.o: %.rc version.h + $(RCmsg)windres -U _WIN64 -F pe-i386 $< $@ + +x64/%.o: %.c ansicon.h + $(CCmsg) $(CC) -m64 -c $(CFLAGS) $< -o $@ + +x64/%v.o: %.rc version.h + $(RCmsg)windres -F pe-x86-64 $< $@ + +x64/%32.o: %.c + $(CCmsg)$(CC) -m32 -DW32ON64 $(CFLAGS) $< -c -o $@ + + +ifeq ($(ARCH),multi) +all: ansicon32 ansicon64 +else +all: ansicon$(ARCH) +endif + +ansicon32: x86 x86/ansicon.exe x86/ANSI32.dll x64 x64/ANSI32.dll + +ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll + +x86: + cmd /c "mkdir x86" + +x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o + $(LDmsg)$(CC) -m32 $+ -s -o $@ + +x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o + $(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC0000 + +x64: + cmd /c "mkdir x64" + +x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o + $(LDmsg)$(CC) -m64 $+ -s -o $@ + +x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o + $(LDmsg)$(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC000000 + +x64/ANSI32.dll: x64/ANSI32.o x64/proctype32.o x86/injdll32.o x86/util.o x86/ansiv.o + $(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared,--image-base,0xAC0000 + +x86/ansicon.o: version.h +x86/ANSI.o: version.h +x86/util.o: version.h +x64/ansicon.o: version.h +x64/ANSI.o: version.h +x64/util.o: version.h + +# Need two commands, because if the directory doesn't exist, it won't delete +# anything at all. +clean: + -cmd /c "del x86\*.o 2>nul" + -cmd /c "del x64\*.o 2>nul" diff --git a/ansicon/makefile.vc b/ansicon/makefile.vc old mode 100755 new mode 100644 index da71872..081982d --- a/ansicon/makefile.vc +++ b/ansicon/makefile.vc @@ -1,88 +1,141 @@ -# VC makefile for ANSICON. -# Jason Hood, 15 November, 2010. - -# I've used Visual C++ 2008 Express for the 32-bit version and the 2003 R2 -# Platform SDK for the 64-bit version. Since these are entirely separate -# environments, define BITS to decide which should be built. Note that the -# 64-bit version still requires the 32-bit ANSI32.dll, so both environments -# are required and you should build the 32-bit version before the 64-bit. - -#BITS = 32 -#BITS = 64 - -!IFNDEF BITS -BITS = 32 -!ENDIF - -!IF $(BITS) == 32 -DIR = x86 -!ELSE -!IF $(BITS) == 64 -DIR = x64 -!ELSE -!ERROR BITS should be defined to 32 or 64. -!ENDIF -!ENDIF - -CC = cl -CFLAGS = /nologo /W3 /Ox /GF /D_CRT_SECURE_NO_WARNINGS -LIBS = advapi32.lib user32.lib - -# This is required for the 2003 Platform SDK, but not for Visual Studio 2010. -#LIBS64 = bufferoverflowu.lib - -X86OBJS = x86\proctype.obj x86\injdll32.obj x86\util.obj -X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj - -{}.c{$(DIR)}.obj: - $(CC) /c $(CFLAGS) /Fo$@ $< - -{}.rc{$(DIR)}.res: - rc /fo$@ $< - -all: ansicon$(BITS) - -ansicon32: x86 x86\ansicon.exe x86\ANSI32.dll x64\ANSI32.dll - -ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll x64\ANSI-LLW.exe - -x86: - mkdir x86 - -x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res - $(CC) /nologo /Fe$@ $** $(LIBS) - -x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res - $(CC) /nologo /LD /Fe$@ $** $(LIBS) - -x64: - mkdir x64 - -x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res - $(CC) /nologo /Fe$@ $** $(LIBS) $(LIBS64) - -x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res - $(CC) /nologo /LD /Fe$@ $** $(LIBS) $(LIBS64) - -x64\ANSI32.dll: x64\ANSI32.obj x64\proctype32.obj x86\injdll32.obj x86\util.obj x86\ansi.res - $(CC) /nologo /LD /Fe$@ $** $(LIBS) - -x64\ANSI-LLW.exe: ANSI-LLW.c - $(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? $(LIBS64) - -ansicon.c: ansicon.h version.h -ansicon.rc: version.h -ANSI.c: ansicon.h version.h -ANSI.rc: version.h -util.c: ansicon.h version.h -injdll32.c: ansicon.h -injdll64.c: ansicon.h -proctype.c: ansicon.h - -x64\ANSI32.obj: ANSI.c - $(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? -x64\proctype32.obj: proctype.c - $(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? - -clean: - -del $(DIR)\*.obj $(DIR)\*.res $(DIR)\*.lib $(DIR)\*.exp +# VC makefile for ANSICON. +# Jason Hood, 15 November, 2010. +# +# Tested with: +# * Visual Studio 6.0 (VC6); +# * Visual C++ 2003 Toolkit (VC7); +# * Platform SDK for Windows Server 2003 R2 (VC8 64-bit); +# * Visual Studio 2008 Express SP1 (VC9); +# * Visual Studio 2010 Professional (VC10). +# +# Note that the 64-bit version still requires a 32-bit compiler. +# +# 22 & 23 November, 2012: +# determine if the PSDK is used automatically; +# use AC0000[00] (AnsiCon) as the base address; +# twiddle stuff around to support VC6 (with 2003 PSDK) for the 32-bit version; +# determine BITS automatically. +# +# 18 July, 2013: +# hide the commands (use V=1 to show them). + +#BITS = 32 +#BITS = 64 + +!IFNDEF BITS +!IF "$(CPU)" == "AMD64" || "$(PLATFORM)" == "x64" +BITS = 64 +!ELSE +BITS = 32 +!ENDIF +!ENDIF + +!IF $(BITS) == 32 +DIR = x86 +!ELSE +!IF $(BITS) == 64 +DIR = x64 +RFLAGS = /D_WIN64 +!ELSE +!ERROR BITS should be defined to 32 or 64. +!ENDIF +!ENDIF + +# This is required for the 2003 Platform SDK, but not for Visual Studio 2010. +!IF "$(_NMAKE_VER)" == "7.00.8882" +!IF $(BITS) == 64 +LIBS64 = bufferoverflowu.lib +# The 2003 Toolkit doesn't have MSVCRT.LIB, but VC98 does. +!ELSEIF !DEFINED(SHARE) && !DEFINED(MSVCDIR) +SHARE = +!ENDIF +!ENDIF + +# Link with MSVCRT.LIB by default. +!IFNDEF SHARE +SHARE = /MD +!ENDIF + +# Manifest tool to embed the manifest required by 2008. +MT = mt.exe + +CFLAGS = /nologo /W3 /O2 $(SHARE) /D_CRT_SECURE_NO_WARNINGS +LIBS = advapi32.lib user32.lib $(LIBS64) + +X86OBJS = x86\proctype.obj x86\injdll32.obj x86\util.obj +X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj + +!IF !DEFINED(V) +V = 0 +!ENDIF +!IF $(V) == 0 +CCmsg = @ +RCmsg = @echo $<& +LDmsg = @echo $@& +MTmsg = @echo Embedding manifest& +!ENDIF + +{}.c{$(DIR)}.obj: + $(CCmsg)$(CC) /c $(CFLAGS) /Fo$@ $< + +{}.rc{$(DIR)}.res: + $(RCmsg)$(RC) $(RFLAGS) /fo$@ $< + +all: ansicon$(BITS) + +ansicon32: x86 x86\ansicon.exe x86\ANSI32.dll x64 x64\ANSI32.dll + +ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll + +x86: + mkdir x86 + +x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res + $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) /link /filealign:512 +!IF "$(_NMAKE_VER)" == "9.00.30729.01" + $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1 + @del $@.manifest +!ENDIF + +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 +!IF "$(_NMAKE_VER)" == "9.00.30729.01" + $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 + @del $@.manifest +!ENDIF + +x64: + mkdir x64 + +x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res + $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) + +x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res + $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link /base:0xAC000000 /section:.shared,s + +x64\ANSI32.dll: x64\ANSI32.obj x64\proctype32.obj x86\injdll32.obj x86\util.obj x86\ansi.res + $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link /base:0xAC0000 /section:.shared,s /filealign:512 +!IF "$(_NMAKE_VER)" == "9.00.30729.01" + $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 + @del $@.manifest +!ENDIF + +ansicon.c: ansicon.h version.h +ansicon.rc: version.h +ANSI.c: ansicon.h version.h +ANSI.rc: version.h +util.c: ansicon.h version.h +injdll32.c: ansicon.h +injdll64.c: ansicon.h +proctype.c: ansicon.h + +x64\ANSI32.obj: ANSI.c + $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? +x64\proctype32.obj: proctype.c + $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? + +clean: + -del $(DIR)\*.obj $(DIR)\*.res $(DIR)\*.lib $(DIR)\*.exp +!IF $(BITS) == 32 + -del x64\ansi32.obj x64\proctype32.obj +!ENDIF diff --git a/ansicon/proctype.c b/ansicon/proctype.c old mode 100755 new mode 100644 index 27399cd..d45fdbc --- a/ansicon/proctype.c +++ b/ansicon/proctype.c @@ -1,105 +1,105 @@ -/* - Test for a valid process. This may sometimes detect GUI, even for a console - process. I think this is due to a DLL being loaded in the address space - before the main image. Ideally I could just use the base address directly, - but that doesn't seem easy to do for another process - there doesn't seem to - be a GetModuleHandle for another process. The CreateRemoteThread trick won't - work with 64-bit (exit code is DWORD) and setting it up to make it work - hardly seems worth it. There's GetModuleInformation, but passing in NULL just - returns a base of NULL, so that's no help. Since 64/32 is sufficient, let - ansicon.exe handle the difference between console/GUI. - - Update: ignore images characterised as DLL. -*/ - -#include "ansicon.h" - - -int ProcessType( LPPROCESS_INFORMATION pinfo, BOOL* gui ) -{ - char* ptr; - MEMORY_BASIC_INFORMATION minfo; - IMAGE_DOS_HEADER dos_header; - IMAGE_NT_HEADERS nt_header; - SIZE_T read; - - *gui = FALSE; - for (ptr = NULL; - VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) ); - ptr += minfo.RegionSize) - { - if (minfo.BaseAddress == minfo.AllocationBase && - ReadProcessMemory( pinfo->hProcess, minfo.AllocationBase, - &dos_header, sizeof(dos_header), &read )) - { - if (dos_header.e_magic == IMAGE_DOS_SIGNATURE) - { - if (ReadProcessMemory( pinfo->hProcess, (char*)minfo.AllocationBase + - dos_header.e_lfanew, &nt_header, - sizeof(nt_header), &read )) - { - if (nt_header.Signature == IMAGE_NT_SIGNATURE && - (nt_header.FileHeader.Characteristics & - (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) - == IMAGE_FILE_EXECUTABLE_IMAGE) - { - *gui = (nt_header.OptionalHeader.Subsystem - == IMAGE_SUBSYSTEM_WINDOWS_GUI); - if (nt_header.OptionalHeader.Subsystem == - IMAGE_SUBSYSTEM_WINDOWS_CUI || *gui) - { - if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) - { - // Microsoft ignores precision on %p. - DEBUGSTR( 1, L" 32-bit %s (base = %.8X)", - (*gui) ? L"GUI" : L"console", - (DWORD)(DWORD_PTR)minfo.AllocationBase ); - return 32; - } -#ifdef _WIN64 - if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) - { - DEBUGSTR( 1, L" 64-bit %s (base = %p)", - (*gui) ? L"GUI" : L"console", minfo.AllocationBase ); - return 64; - } -#elif defined(W32ON64) - if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) - { - DEBUGSTR( 1, L" 64-bit %s", - (*gui) ? L"GUI" : L"console" ); - return 64; - } -#endif - DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)", - nt_header.FileHeader.Machine ); - } - else - { - DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)", - nt_header.OptionalHeader.Subsystem ); - } - return 0; - } - } - } - } -#ifdef _WIN32 - // If a 32-bit process manages to load a 64-bit one, we may miss the base - // address. If the pointer overflows, assume 64-bit and abort. - if (ptr > ptr + minfo.RegionSize) - { -#ifdef W32ON64 - DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit console" ); - return 64; -#else - DEBUGSTR( 1, L" Ignoring apparent 64-bit process" ); - return 0; -#endif - } -#endif - } - - DEBUGSTR( 1, L" Ignoring non-Windows process" ); - return 0; -} +/* + Test for a valid process. This may sometimes detect GUI, even for a console + process. I think this is due to a DLL being loaded in the address space + before the main image. Ideally I could just use the base address directly, + but that doesn't seem easy to do for another process - there doesn't seem to + be a GetModuleHandle for another process. The CreateRemoteThread trick won't + work with 64-bit (exit code is DWORD) and setting it up to make it work + hardly seems worth it. There's GetModuleInformation, but passing in NULL just + returns a base of NULL, so that's no help. Since 64/32 is sufficient, let + ansicon.exe handle the difference between console/GUI. + + Update: ignore images characterised as DLL. +*/ + +#include "ansicon.h" + + +int ProcessType( LPPROCESS_INFORMATION pinfo, BOOL* gui ) +{ + char* ptr; + MEMORY_BASIC_INFORMATION minfo; + IMAGE_DOS_HEADER dos_header; + IMAGE_NT_HEADERS nt_header; + SIZE_T read; + + *gui = FALSE; + for (ptr = NULL; + VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) ); + ptr += minfo.RegionSize) + { + if (minfo.BaseAddress == minfo.AllocationBase && + ReadProcessMemory( pinfo->hProcess, minfo.AllocationBase, + &dos_header, sizeof(dos_header), &read )) + { + if (dos_header.e_magic == IMAGE_DOS_SIGNATURE) + { + if (ReadProcessMemory( pinfo->hProcess, (char*)minfo.AllocationBase + + dos_header.e_lfanew, &nt_header, + sizeof(nt_header), &read )) + { + if (nt_header.Signature == IMAGE_NT_SIGNATURE && + (nt_header.FileHeader.Characteristics & + (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) + == IMAGE_FILE_EXECUTABLE_IMAGE) + { + *gui = (nt_header.OptionalHeader.Subsystem + == IMAGE_SUBSYSTEM_WINDOWS_GUI); + if (nt_header.OptionalHeader.Subsystem == + IMAGE_SUBSYSTEM_WINDOWS_CUI || *gui) + { + if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) + { + // Microsoft ignores precision on %p. + DEBUGSTR( 1, L" 32-bit %s (base = %.8X)", + (*gui) ? L"GUI" : L"console", + (DWORD)(DWORD_PTR)minfo.AllocationBase ); + return 32; + } +#ifdef _WIN64 + if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) + { + DEBUGSTR( 1, L" 64-bit %s (base = %p)", + (*gui) ? L"GUI" : L"console", minfo.AllocationBase ); + return 64; + } +#elif defined(W32ON64) + if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) + { + DEBUGSTR( 1, L" 64-bit %s", + (*gui) ? L"GUI" : L"console" ); + return 64; + } +#endif + DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)", + nt_header.FileHeader.Machine ); + } + else + { + DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)", + nt_header.OptionalHeader.Subsystem ); + } + return 0; + } + } + } + } +#ifndef _WIN64 + // If a 32-bit process loads a 64-bit one, we may miss the base + // address. If the pointer overflows, assume 64-bit. + if (((DWORD)ptr >> 12) + ((DWORD)minfo.RegionSize >> 12) > 0x80000) + { +#ifdef W32ON64 + DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit console" ); + return 64; +#else + DEBUGSTR( 1, L" Ignoring apparent 64-bit process" ); + return 0; +#endif + } +#endif + } + + DEBUGSTR( 1, L" Ignoring non-Windows process" ); + return 0; +} diff --git a/ansicon/readme.txt b/ansicon/readme.txt old mode 100755 new mode 100644 index 78f00eb..b092aa3 --- a/ansicon/readme.txt +++ b/ansicon/readme.txt @@ -1,439 +1,465 @@ - - ANSICON - - Copyright 2005-2012 Jason Hood - - Version 1.53. Freeware - - - =========== - Description - =========== - - ANSICON provides ANSI escape sequences for Windows console programs. It - provides much the same functionality as `ANSI.SYS' does for MS-DOS. - - - ============ - Requirements - ============ - - Windows 2000 Professional and later (it won't work with NT or 9X). - - - ============ - Installation - ============ - - Add x86 (if your OS is 32-bit) or x64 (if 64-bit) to your PATH, or copy - the relevant files to a directory already on the PATH. Alternatively, - use option `-i' (or `-I') to install it permanently, by adding an entry - to CMD.EXE's AutoRun registry value (current user or local machine, - respectively). Uninstall simply involves closing any programs that are - currently using it, running with `-u' (and/or `-U') to remove the Auto- - Run entry/ies, then removing the directory from PATH or deleting the - files. No other changes are made. - - --------- - Upgrading - --------- - - Delete ANSI.dll, it has been replaced with ANSI32.dll. - Delete ANSI-LLA.dll, it has been replaced with ANSI-LLW.dll. - Uninstall a pre-1.50 version and reinstall with this version. - - - ===== - Usage - ===== - - Options (case sensitive): - - -l Log to %temp%\ansicon.log. - - -p Enable the parent process (i.e. the command shell used to - run ANSICON) to recognise escapes. - - -m Set the current (and default) attribute to grey on black - ("monochrome"), or the attribute following the `m' (please - use `COLOR /?' for attribute values). - - -e Echo the command line - a space or tab after the `e' is - ignored, the remainder is displayed verbatim. - - -E As above, but no newline is added. - - -t Display ("type") each file (or standard input if none or the - name is "-") as though they are a single file. - - -T Display "==> FILE NAME <==", a blank line (or an error - message), the file and another blank line. - - Running ANSICON with no arguments will start a new instance of the com- - mand processor (the program defined by the `ComSpec' environment var- - iable, typically `CMD.EXE'), or display standard input if it is redir- - ected. Any argument will be treated as a program and its arguments. - - Eg: `ansicon -m30 -t file.ans' will display `file.ans' using black on - cyan as the default color. - - The attribute may start with "-" to permanently reverse the foreground - and background colors (but not when using `-p'). Eg: `ansicon -m-f0 -t - file.log' will use reversed black on white as the default (i.e. white on - black, with foreground sequences changing the background). - - If you experience trouble with certain programs, the log may help in - finding the cause; it can be found at "%TEMP%\ansicon.log". A number - should follow the `l': - - 0 No logging - 1 Log process start and end - 2 Above, plus log modules used by the process - 3 Above, plus log functions that are hooked - 4 Log console output (add to any of the above) - 8 Append to the existing file (add to any of the above) - 16 Log all imported modules (add to any of the above) - - The log option will not work with `-p'; set the environment variable - ANSICON_LOG instead. The variable is only read once when a new process - is started; changing it won't affect running processes. If you identify - a module that causes problems (one known is "nvd3d9wrap.dll") add it to - the ANSICON_EXC environment variable (see ANSICON_API below, but the - extension is required). - - Once installed, the ANSICON environment variable will be created. This - variable is of the form "WxH (wxh)", where W & H are the width and - height of the buffer and w & h are the width and height of the window. - The variable is updated whenever a program reads it directly (i.e. as - an individual request, not as part of the entire environment block). - For example, "set an" will not update it, but "echo %ansicon%" will. - Also created is ANSICON_VER, which contains the version without the - point (1.50 becomes "150"). This variable does not exist as part of the - environment block ("set an" will not show it). - - If installed, GUI programs will not be hooked. Either start the program - directly with `ansicon', or add it to the ANSICON_GUI variable (see - ANSICON_API below). - - Using `ansicon' after install will always start with the default attrib- - utes, restoring the originals on exit; all other programs will use the - current attributes. The shift state is always reset for a new process. - - The Windows API WriteFile and WriteConsoleA functions will set the num- - ber of characters written, not the number of bytes. When using a multi- - byte character set, this results in a smaller number (since multiple - bytes are used to represent a single character). Some programs recog- - nise this as a reduced write and will inadvertently repeat previous - characters. If you discover such a program, use the ANSICON_API envir- - onment variable to record it and override the API, returning the origin- - al byte count. Ruby is an example of such a program (at least, up till - 1.9.2p0), so use "set ANSICON_API=ruby" to avoid the repitition. The - full syntax of the variable is: - - ANSICON_API=[!]program;program;program... - - PROGRAM is the name of the program, with no path and extension. The - leading exclamation inverts the usage, meaning the API will always be - overridden, unless the program is in the list. The variable can be made - permanent by going to System Properties, selecting the Advanced tab and - clicking Environment Variables (using XP; Vista/7 may be different). - - - ==================== - Sequences Recognised - ==================== - - The following escape sequences are recognised. - - \e]0;titleBEL Set (xterm) window's title (and icon) - \e[21t Report (xterm) window's title - \e[s Save Cursor - \e[u Restore Cursor - \e[#G CHA Cursor Character Absolute - \e[#E CNL Cursor Next Line - \e[#F CPL Cursor Preceding Line - \e[#D CUB Cursor Left - \e[#B CUD Cursor Down - \e[#C CUF Cursor Right - \e[#;#H CUP Cursor Position - \e[#A CUU Cursor Up - \e[#P DCH Delete Character - \e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor) - \e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor) - \e[#M DL Delete Line - \e[#n DSR Device Status Report - \e[#X ECH Erase Character - \e[#J ED Erase In Page - \e[#K EL Erase In Line - \e[#` HPA Character Position Absolute - \e[#j HPB Character Position Backward - \e[#a HPR Character Position Forward - \e[#;#f HVP Character And Line Position - \e[#@ ICH Insert Character - \e[#L IL Insert Line - SI LS0 Locking-shift Zero (see below) - SO LS1 Locking-shift One - \e[#;#;#m SGR Select Graphic Rendition - \e[#d VPA Line Position Absolute - \e[#k VPB Line Position Backward - \e[#e VPR Line Position Forward - - `\e' represents the escape character (ASCII 27); `#' represents a - decimal number (optional, in most cases defaulting to 1); BEL, SO and - SI are ASCII 7, 14 and 15. Regarding SGR: bold will set the foreground - intensity; underline and blink will set the background intensity; - conceal uses background as foreground. - - I make a distinction between "\e[m" and "\e[0;...m". Both will restore - the original foreground/background colors (and so "0" should be the - first parameter); the former will also restore the original bold and - underline attributes, whilst the latter will explicitly reset them. The - environment variable ANSICON_DEF can be used to change the default col- - ors (same value as `-m'; setting the variable does not change the cur- - rent colors). - - - ================= - Sequences Ignored - ================= - - The following escape sequences are explicitly ignored. - - \e(? Designate G0 character set (`?' is anything). - \e)? Designate G1 character set (`?' is anything). - \e[?... Private sequence - \e[>... Private sequence - - The G0 character set is always ASCII; the G1 character set is always - the DEC Special Graphics Character Set. - - - ================================== - DEC Special Graphics Character Set - ================================== - - This is my interpretation of the set, as shown by - http://vt100.net/docs/vt220-rm/table2-4.html. - - - Char Unicode Code Point & Name - ---- ------------------------- - _ U+0020 Space (blank) - ` U+2666 Black Diamond Suit - a U+2592 Medium Shade - b U+2409 Symbol For Horizontal Tabulation - c U+240C Symbol For Form Feed - d U+240D Symbol For Carriage Return - e U+240A Symbol For Line Feed - f U+00B0 Degree Sign - g U+00B1 Plus-Minus Sign - h U+2424 Symbol For Newline - i U+240B Symbol For Vertical Tabulation - j U+2518 Box Drawings Light Up And Left - k U+2510 Box Drawings Light Down And Left - l U+250C Box Drawings Light Down And Right - m U+2514 Box Drawings Light Up And Right - n U+253C Box Drawings Light Vertical And Horizontal - o U+00AF Macron (SCAN 1) - p U+25AC Black Rectangle (SCAN 3) - q U+2500 Box Drawings Light Horizontal (SCAN 5) - r U+005F Low Line (SCAN 7) - s U+005F Low Line (SCAN 9) - t U+251C Box Drawings Light Vertical And Right - u U+2524 Box Drawings Light Vertical And Left - v U+2534 Box Drawings Light Up And Horizontal - w U+252C Box Drawings Light Down And Horizontal - x U+2502 Box Drawings Light Vertical - y U+2264 Less-Than Or Equal To - z U+2265 Greater-Than Or Equal To - { U+03C0 Greek Small Letter Pi - | U+2260 Not Equal To - } U+00A3 Pound Sign - ~ U+00B7 Middle Dot - - - G1.txt is a Unicode file to view the glyphs "externally". G1.bat is a - batch file (using `x86\ansicon') to show the glyphs in the console. The - characters will appear as they should using Lucida (other than the Sym- - bols), but code page will influence them when using a raster font (but - of particular interest, 437 and 850 both show the Box Drawings). - - - =========== - Limitations - =========== - - The entire console buffer is used, not just the visible window. - - Building rubyinstaller on Win7 crashes (XP is fine). - - - =============== - Version History - =============== - - Legend: + added, - bug-fixed, * changed. - - 1.53 - 12 June, 2012: - - fix for multiple simultaneous process creation (e.g. "cl /MP ..."). - - 1.52 - 2 June, 2012: - + 32-bit processes can inject into 64-bit processes; - + implemented \e[39m & \e[49m (set default foreground/background color); - + added \e[#X, \e[#`, \e[#a, \e[#d, \e[#e, \[e#j and \e[#k; - * changed sequence descriptions to those in ECMA-48, ordered by acronym. - - 1.51 - 24 February, 2012: - - fixed installing into a piped/redirected CMD.EXE; - - fixed 32-bit process trying to identify a 64-bit process; - - ignore version within core API DLL names (now Win8 works); - + hook _lwrite & _hwrite (now Silverfrost FTN95 v6.20 works). - - 1.50 - 14 December, 2011: - - -u does not imply -p; - - return the program's exit code; - - -p by itself will not restore original color; - - output error messages to stderr; - * logging is always available, with various levels; include the pid; - * don't automatically hook GUI programs, use `ansicon' or ANSICON_GUI; - * always place first in AutoRun; don't run if already installed; - + global reverse video capability; - + added ANSICON_VER to provide version/install test; - + added ANSICON_EXC to exclude selected modules; - + added ANSICON_DEF to explicitly set the default SGM. - - 1.40 - 1 March, 2011: - - hook GetProcAddress (now PowerShell works); - + add SO/SI, using the DEC Special Graphics Character Set for G1; - + add DECTCEM to show/hide the cursor. - - 1.32 - 22 December, 2010: - - fixed crash due to NULL lpNumberOfBytesWritten/lpNumberOfCharsWritten; - - -p will test the parent process for validity; - * hook into GUI processes; - + recognise DSR and xterm window title sequences; - - fixed MinGW32 binaries (LLW was wrong). - - 1.31 - 19 November, 2010: - - fixed multibyte support (no extra junk with UTF-8 files); - * provide workaround for API byte/character differences; - * fixed potential problem if install path uses Unicode. - - 1.30 - 7 September, 2010: - + x64 version. - - 1.25 - 22 July, 2010: - - hook LoadLibraryEx (now CScript works); - - fixed -i when AutoRun existed, but was empty; - + support for Windows 7; - + -I (and -U) use HKEY_LOCAL_MACHINE. - - 1.24 - 7 January, 2010: - - fix -t and -e when ANSICON was already running; - + read standard input if redirected with no arguments, if -t has no - files, or if the name is "-" (which also serves as a workaround for - programs that don't get hooked, such as CScript). - - 1.23 - 11 November, 2009: - - restore hooked functions when unloading; - - reverse the "bold" and "underline" settings; - * conceal characters by making foreground color same as background. - - 1.22 - 5 October, 2009: - - hook LoadLibrary to inject into applications started via association. - - 1.21 - 23 September, 2009: - + -i (and -u) option to add (remove) entry to AutoRun value. - - 1.20 - 21 June, 2009: - * use another injection method; - + create ANSICON environment variable; - + -e (and -E) option to echo the command line (without newline); - + -t (and -T) option to type (display) files (with file name). - - 1.15 - 17 May, 2009: - - fix output corruption for long (over 8192 characters) ANSI strings. - - 1.14 - 3 April, 2009: - - fix the test for an empty import section (eg. XCOPY now works). - - 1.13 - 21 & 27 March, 2009: - * use a new injection method (to work with DEP); - * use Unicode. - - 1.12 - 9 March, 2009: - - fix processing child programs (generate a relocatable DLL). - - 1.11 - 28 February, 2009: - - fix processing child programs (only use for console executables). - - 1.10 - 22 February, 2009: - - fix output corruption (buffer overflow in MyConsoleWriteW); - - recognise current screen attributes as current ANSI atrributes; - - ignore Ctrl+C and Ctrl+Break; - + process child programs. - - 1.01 - 12 March, 2006: - * \e[m will restore original color, not set grey on black; - + -m option to set default (and initial) color; - - restore original color on exit; - - disable escape processing when console has disabled processed output; - + \e[5m (blink) is the same as \e[4m (underline); - - do not conceal control characters (0 to 31). - - 1.00 - 23 October, 2005: - + initial release. - - - =============== - Acknowledgments - =============== - - Jean-Louis Morel, for his Perl package Win32::Console::ANSI. It - provided the basis of `ANSI.dll'. - - Sergey Oblomov (hoopoepg), for Console Manager. It provided the basis - of `ansicon.exe'. - - Anton Bassov's article "Process-wide API spying - an ultimate hack" in - "The Code Project". - - Richard Quadling - his persistence in finding bugs has made ANSICON - what it is today. - - Dmitry Menshikov, Marko Bozikovic and Philippe Villiers, for their - assistance in making the 64-bit version a reality. - - Luis Lavena and the Ruby people for additional improvements. - - Leigh Hebblethwaite for documentation tweaks. - - - ======= - Contact - ======= - - mailto:jadoxa@yahoo.com.au - http://ansicon.adoxa.cjb.net/ - https://github.com/adoxa/ansicon - - Jason Hood - 11 Buckle Street - North Rockhampton - Qld 4701 - Australia - - - ============ - Distribution - ============ - - The original zipfile can be freely distributed, by any means. However, - I would like to be informed if it is placed on a CD-ROM (other than an - archive compilation; permission is granted, I'd just like to know). - Modified versions may be distributed, provided it is indicated as such - in the version text and a source diff is included. - - - ========================== - Jason Hood, 12 June, 2012. + + ANSICON + + Copyright 2005-2013 Jason Hood + + Version 1.66. Freeware + + +Description +=========== + + ANSICON provides ANSI escape sequences for Windows console programs. It + provides much the same functionality as 'ANSI.SYS' does for MS-DOS. + + +Requirements +============ + + 32-bit: Windows 2000 Professional and later (it won't work with NT or 9X). + 64-bit: Vista and later (it won't work with XP64). + + +Installation +============ + + Add "x86" (if your OS is 32-bit) or "x64" (if 64-bit) to your PATH, or copy + the relevant files to a directory already on the PATH. Alternatively, use + option '-i' (or '-I', if permitted) to install it permanently, by adding an + entry to CMD.EXE's AutoRun registry value (current user or local machine, + respectively). + + Uninstall simply involves closing any programs that are currently using it; + running with '-u' (and/or '-U') to remove it from AutoRun; removing the + directory from PATH; and deleting the files. No other changes are made + (although you may have also created environment variables). + +Upgrading +--------- + + Delete ANSI.dll, it has been replaced with ANSI32.dll. + Delete ANSI-LLA.exe and ANSI-LLW.exe, they are no longer used. + Uninstall a pre-1.50 version and reinstall with this version. + + +Usage +===== + + Options (case sensitive): + + -l Log to "%TEMP%\ansicon.log". + + -p Enable the parent process (i.e. the command shell used to run + ANSICON) to recognise escapes. + + -m Set the current (and default) attribute to grey on black + ("monochrome"), or the attribute following the 'm' (please + use 'COLOR /?' for attribute values). + + -e Echo the command line - a space or tab after the 'e' is + ignored, the remainder is displayed verbatim. + + -E As above, but no newline is added. + + -t Display ("type") each file (or standard input if none or the + name is "-") as though they are a single file. + + -T Display "==> FILE NAME <==", a blank line (or an error + message), the file and another blank line. + + Running ANSICON with no arguments will start a new instance of the command + processor (the program defined by the 'ComSpec' environment variable, typ- + ically 'CMD.EXE'), or display standard input if it is redirected. Any arg- + ument will be treated as a program and its arguments. + + E.g.: 'ansicon -m30 -t file.ans' will display "file.ans" using black on + cyan as the default color. + + The attribute may start with '-' to permanently reverse the foreground and + background colors (but not when using '-p'). E.g.: 'ansicon -m-f0 -t + file.log' will use reversed black on white as the default (i.e. white on + black, with foreground sequences changing the background). + + If you experience trouble with certain programs, the log may help in find- + ing the cause; it can be found at "%TEMP%\ansicon.log". A number should + follow the 'l': + + 0 No logging + 1 Log process start and end + 2 Above, plus log modules used by the process + 3 Above, plus log functions that are hooked + 4 Log console output (add to any of the above) + 8 Append to the existing file (add to any of the above) + 16 Log all imported modules (add to any of the above) + + The log option will not work with '-p'; set the environment variable + ANSICON_LOG instead. The variable is only read once when a new process is + started; changing it won't affect running processes. If you identify a + module that causes problems, add it to the ANSICON_EXC environment variable + (see ANSICON_API below, but the extension is required). + + E.g.: 'ansicon -l5' will start a new command processor, logging every pro- + cess it starts along with their output. + + Once installed, the ANSICON environment variable will be created. This + variable is of the form "WxH (wxh)", where 'W' & 'H' are the width and + height of the buffer and 'w' & 'h' are the width and height of the window. + The variable is updated whenever a program reads it directly (i.e. as an + individual request, not as part of the entire environment block). For + example, 'set an' will not update it, but 'echo %ansicon%' will. Also + created is ANSICON_VER, which contains the version without the point (1.50 + becomes "150"). This variable does not exist as part of the environment + block ('set an' will not show it). + + If installed, GUI programs will not be hooked. Either start the program + directly with 'ansicon', or add it to the ANSICON_GUI variable (see + ANSICON_API below). + + Using 'ansicon' after install will always start with the default attrib- + utes, restoring the originals on exit; all other programs will use the cur- + rent attributes. The shift state is always reset for a new process. + + The Windows API WriteFile and WriteConsoleA functions will set the number + of characters written, not the number of bytes. When using a multibyte + character set, this results in a smaller number (since multiple bytes are + used to represent a single character). Some programs recognise this as a + reduced write and will inadvertently repeat previous characters. If you + discover such a program, use the ANSICON_API environment variable to record + it and override the API, returning the original byte count. Ruby (prior to + 1.9.3) is an example of such a program, so use 'set ANSICON_API=ruby' to + avoid the repitition. The full syntax is: + + ANSICON_API=[!]program;program;program... + + PROGRAM is the name of the program, with no path and extension. The lead- + ing exclamation inverts the usage, meaning the API will always be over- + ridden, unless the program is in the list. The variable can be made perm- + anent by going to System Properties, selecting the Advanced tab (with Vista + onwards, this can be done by running "SystemPropertiesAdvanced") and click- + ing Environment Variables. + + +Sequences Recognised +==================== + + The following escape sequences are recognised. + + \e]0;titleBEL Set (xterm) window's title (and icon) + \e[21t Report (xterm) window's title + \e[s Save Cursor + \e[u Restore Cursor + \e[#G CHA Cursor Character Absolute + \e[#E CNL Cursor Next Line + \e[#F CPL Cursor Preceding Line + \e[#D CUB Cursor Left + \e[#B CUD Cursor Down + \e[#C CUF Cursor Right + \e[#;#H CUP Cursor Position + \e[#A CUU Cursor Up + \e[#P DCH Delete Character + \e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor) + \e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor) + \e[#M DL Delete Line + \e[#n DSR Device Status Report + \e[#X ECH Erase Character + \e[#J ED Erase In Page + \e[#K EL Erase In Line + \e[#` HPA Character Position Absolute + \e[#j HPB Character Position Backward + \e[#a HPR Character Position Forward + \e[#;#f HVP Character And Line Position + \e[#@ ICH Insert Character + \e[#L IL Insert Line + SI LS0 Locking-shift Zero (see below) + SO LS1 Locking-shift One + \e[#;#;#m SGR Select Graphic Rendition + \e[#d VPA Line Position Absolute + \e[#k VPB Line Position Backward + \e[#e VPR Line Position Forward + + '\e' represents the escape character (ASCII 27); '#' represents a decimal + number (optional, in most cases defaulting to 1); BEL, SO and SI are ASCII + 7, 14 and 15. Regarding SGR: bold will set the foreground intensity; blink + and underline will set the background intensity; conceal uses background as + foreground. See "sequences.txt" for a more complete description. + + I make a distinction between '\e[m' and '\e[0;...m'. Both will restore the + original foreground/background colors (and so '0' should be the first para- + meter); the former will also restore the original bold and underline attri- + butes, whilst the latter will explicitly reset them. The environment var- + iable ANSICON_DEF can be used to change the default colors (same value as + '-m'; setting the variable does not change the current colors). + + +Sequences Ignored +================= + + The following escape sequences are explicitly ignored. + + \e(? Designate G0 character set ('?' is any character). + \e)? Designate G1 character set ('?' is any character). + \e[?... Private sequence + \e[>... Private sequence + + The G0 character set is always ASCII; the G1 character set is always the + DEC Special Graphics Character Set. + + +DEC Special Graphics Character Set +================================== + + This is my interpretation of the set, as shown by + http://vt100.net/docs/vt220-rm/table2-4.html. + + + Char Unicode Code Point & Name + ---- ------------------------- + _ U+0020 Space (blank) + ` U+2666 Black Diamond Suit + a U+2592 Medium Shade + b U+2409 Symbol For Horizontal Tabulation + c U+240C Symbol For Form Feed + d U+240D Symbol For Carriage Return + e U+240A Symbol For Line Feed + f U+00B0 Degree Sign + g U+00B1 Plus-Minus Sign + h U+2424 Symbol For Newline + i U+240B Symbol For Vertical Tabulation + j U+2518 Box Drawings Light Up And Left + k U+2510 Box Drawings Light Down And Left + l U+250C Box Drawings Light Down And Right + m U+2514 Box Drawings Light Up And Right + n U+253C Box Drawings Light Vertical And Horizontal + o U+00AF Macron (SCAN 1) + p U+25AC Black Rectangle (SCAN 3) + q U+2500 Box Drawings Light Horizontal (SCAN 5) + r U+005F Low Line (SCAN 7) + s U+005F Low Line (SCAN 9) + t U+251C Box Drawings Light Vertical And Right + u U+2524 Box Drawings Light Vertical And Left + v U+2534 Box Drawings Light Up And Horizontal + w U+252C Box Drawings Light Down And Horizontal + x U+2502 Box Drawings Light Vertical + y U+2264 Less-Than Or Equal To + z U+2265 Greater-Than Or Equal To + { U+03C0 Greek Small Letter Pi + | U+2260 Not Equal To + } U+00A3 Pound Sign + ~ U+00B7 Middle Dot + + G1.txt is a Unicode file to view the glyphs "externally". G1.bat is a + batch file (using 'x86\ansicon') to show the glyphs in the console. The + characters will appear as they should using Lucida (other than the Sym- + bols), but code page will influence them when using a raster font (but of + particular interest, 437 and 850 both show the Box Drawings). + + +Limitations +=========== + + The entire console buffer is used, not just the visible window. + + There's a conflict with NVIDIA's drivers, requiring the setting of the + Environment Variable: + + ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll + + +Version History +=============== + + Legend: + added, - bug-fixed, * changed. + + 1.66 - 20 September, 2013: + - fix 32-bit process trying to detect 64-bit process. + + 1.65 - 4 September, 2013: + - fix finding 32-bit LoadLibraryW address from 64-bit; + - fix \e[K (was using window, not buffer). + + 1.64 - 2 August, 2013: + - improved detection of console output. + + 1.63 - 25 July, 2013: + - don't write the reset sequence (when it's already installed) if output is + redirected. + + 1.62 - 18 July, 2013: + - indicate if opening HKLM failed; + * removed ANSI-LLW.exe again, properly this time; + * add the architecture (32- or 64-bit) to the log. + + 1.61 - 14 February, 2013: + * revert back to using ANSI-LLW.exe, as the new method was unreliable. + + 1.60 - 24 November, 2012: + * new method to get the 32-bit LoadLibraryW address from 64-bit code. + This removes the need for ANSI-LLW.exe, which caused lots of virus + warnings, for some reason. + - set the code page to display some file names properly; + + expand wildcards for -t (ignoring directories and hidden/binary files). + + 1.53 - 12 June, 2012: + - fix for multiple simultaneous process creation (e.g. "cl /MP ..."). + + 1.52 - 2 June, 2012: + + 32-bit processes can inject into 64-bit processes; + + implemented \e[39m & \e[49m (set default foreground/background color); + + added \e[#X, \e[#`, \e[#a, \e[#d, \e[#e, \[e#j and \e[#k; + * changed sequence descriptions to those in ECMA-48, ordered by acronym. + + 1.51 - 24 February, 2012: + - fixed installing into a piped/redirected CMD.EXE; + - fixed 32-bit process trying to identify a 64-bit process; + - ignore version within core API DLL names (now Win8 works); + + hook _lwrite & _hwrite (now Silverfrost FTN95 v6.20 works). + + 1.50 - 14 December, 2011: + - -u does not imply -p; + - return the program's exit code; + - -p by itself will not restore original color; + - output error messages to stderr; + * logging is always available, with various levels; include the pid; + * don't automatically hook GUI programs, use 'ansicon' or ANSICON_GUI; + * always place first in AutoRun; don't run if already installed; + + global reverse video capability; + + added ANSICON_VER to provide version/install test; + + added ANSICON_EXC to exclude selected modules; + + added ANSICON_DEF to explicitly set the default SGM. + + 1.40 - 1 March, 2011: + - hook GetProcAddress (now PowerShell works); + + add SO/SI, using the DEC Special Graphics Character Set for G1; + + add DECTCEM to show/hide the cursor. + + 1.32 - 22 December, 2010: + - fixed crash due to NULL lpNumberOfBytesWritten/lpNumberOfCharsWritten; + - -p will test the parent process for validity; + * hook into GUI processes; + + recognise DSR and xterm window title sequences; + - fixed MinGW32 binaries (LLW was wrong). + + 1.31 - 19 November, 2010: + - fixed multibyte support (no extra junk with UTF-8 files); + * provide workaround for API byte/character differences; + * fixed potential problem if install path uses Unicode. + + 1.30 - 7 September, 2010: + + x64 version. + + 1.25 - 22 July, 2010: + - hook LoadLibraryEx (now CScript works); + - fixed -i when AutoRun existed, but was empty; + + support for Windows 7; + + -I (and -U) use HKEY_LOCAL_MACHINE. + + 1.24 - 7 January, 2010: + - fix -t and -e when ANSICON was already running; + + read standard input if redirected with no arguments, if -t has no + files, or if the name is "-" (which also serves as a workaround for + programs that don't get hooked, such as CScript). + + 1.23 - 11 November, 2009: + - restore hooked functions when unloading; + - reverse the "bold" and "underline" settings; + * conceal characters by making foreground color same as background. + + 1.22 - 5 October, 2009: + - hook LoadLibrary to inject into applications started via association. + + 1.21 - 23 September, 2009: + + -i (and -u) option to add (remove) entry to AutoRun value. + + 1.20 - 21 June, 2009: + * use another injection method; + + create ANSICON environment variable; + + -e (and -E) option to echo the command line (without newline); + + -t (and -T) option to type (display) files (with file name). + + 1.15 - 17 May, 2009: + - fix output corruption for long (over 8192 characters) ANSI strings. + + 1.14 - 3 April, 2009: + - fix the test for an empty import section (eg. XCOPY now works). + + 1.13 - 21 & 27 March, 2009: + * use a new injection method (to work with DEP); + * use Unicode. + + 1.12 - 9 March, 2009: + - fix processing child programs (generate a relocatable DLL). + + 1.11 - 28 February, 2009: + - fix processing child programs (only use for console executables). + + 1.10 - 22 February, 2009: + - fix output corruption (buffer overflow in MyConsoleWriteW); + - recognise current screen attributes as current ANSI atrributes; + - ignore Ctrl+C and Ctrl+Break; + + process child programs. + + 1.01 - 12 March, 2006: + * \e[m will restore original color, not set grey on black; + + -m option to set default (and initial) color; + - restore original color on exit; + - disable escape processing when console has disabled processed output; + + \e[5m (blink) is the same as \e[4m (underline); + - do not conceal control characters (0 to 31). + + 1.00 - 23 October, 2005: + + initial release. + + +Acknowledgments +=============== + + Jean-Louis Morel, for his Perl package Win32::Console::ANSI. It provided + the basis of 'ANSI.dll'. + + Sergey Oblomov (hoopoepg), for Console Manager. It provided the basis of + 'ansicon.exe'. + + Anton Bassov's article "Process-wide API spying - an ultimate hack" in "The + Code Project". + + Richard Quadling - his persistence in finding bugs has made ANSICON what it + is today. + + Dmitry Menshikov, Marko Bozikovic and Philippe Villiers, for their assis- + tance in making the 64-bit version a reality. + + Luis Lavena and the Ruby people for additional improvements. + + Leigh Hebblethwaite for documentation tweaks. + + Vincent Fatica for pointing out \e[K was not right. + + +Contact +======= + + mailto:jadoxa@yahoo.com.au + http://ansicon.adoxa.vze.com/ + https://github.com/adoxa/ansicon + + Jason Hood + 11 Buckle Street + North Rockhampton + Qld 4701 + Australia + + +Distribution +============ + + The original zipfile can be freely distributed, by any means. However, I + would like to be informed if it is placed on a CD-ROM (other than an arch- + ive compilation; permission is granted, I'd just like to know). Modified + versions may be distributed, provided it is indicated as such in the ver- + sion text and a source diff is made available. In particular, the supplied + binaries are freely redistributable. A formal license (zlib) is available + in LICENSE.txt. + + +=============================== +Jason Hood, 20 September, 2013. diff --git a/ansicon/sequences.txt b/ansicon/sequences.txt new file mode 100644 index 0000000..c7de7e9 --- /dev/null +++ b/ansicon/sequences.txt @@ -0,0 +1,118 @@ + + ANSICON + Version 1.60 + +This is a complete list of the ANSI escape sequences recognised by ANSICON, +roughly ordered by function. The initial escape character is assumed. + + +[m restore default color (and intensity) +[0m as above +[...m set attributes (any of these numbers, separated by semicolons): + 0 all attributes off + 1 bold (foreground is intense) + 4 underline (background is intense) + 5 blink (background is intense) + 7 reverse video + 8 concealed (foreground becomes background) + 22 bold off (foreground is not intense) + 24 underline off (background is not intense) + 25 blink off (background is not intense) + 27 normal video + 28 concealed off + 30 foreground black + 31 foreground red + 32 foreground green + 33 foreground yellow + 34 foreground blue + 35 foreground magenta + 36 foreground cyan + 37 foreground white + 39 default foreground (using current intensity) + 40 background black + 41 background red + 42 background green + 43 background yellow + 44 background blue + 45 background magenta + 46 background cyan + 47 background white + 49 default background (using current intensity) + +[J erase from cursor to the end of display +[0J as above +[1J erase from the start of diplay to cursor (inclusive) +[2J erase display and move cursor to the top-left + +[K erase from cursor to the end of line +[0K as above +[1K erase from the start of line to cursor (inclusive) +[2K erase line + +[X erase one character +[#X erase # characters + +[L insert one blank line +[#L insert # blank lines + +[M delete one line +[#M delete # lines + +[P delete one character +[#P delete # characters + +[@ insert one blank character +[#@ insert # blank characters + +[A move cursor up one line +[#A move cursor up # lines +[B move cursor down one line +[#B move cursor down # lines +[C move cursor right one character +[#C move cursor right # characters +[D move cursor left one character +[#D move cursor left # characters + +[k move cursor up one line +[#k move cursor up # lines +[e move cursor down one line +[#e move cursor down # lines +[a move cursor right one character +[#a move cursor right # characters +[j move cursor left one character +[#j move cursor left # characters + +[E move cursor down one line and to first column +[#E move cursor down # lines and to first column +[F move cursor up one line and to first column +[#F move cursor up # lines and to first column + +[G move cursor to first column +[#G move cursor to column # + +[` move cursor to first column +[#` move cursor to column # + +[d move cursor to first line +[#d move cursor to line # + +[H move cursor to top-left +[#H move cursor to line # and first column +[#;#H move cursor to line #, column # + +[f move cursor to top-left +[#f move cursor to line # and first column +[#;#f move cursor to line #, column # + +[s save cursor position +[u move cursor to saved position + +[?25h show cursor +[?25l hide cursor + +[5n sends "\e[0n" to console input (where \e is escape) +[6n sends "\e[#;#R" (line & column) to console input +[21t sends "\e]lTitle\e\" (the console's window title) to console input +]0;TitleST + sets the console title to "Title"; ST (string terminator) is either + character 7 (BEL) or escape and backslash diff --git a/ansicon/util.c b/ansicon/util.c old mode 100755 new mode 100644 index 461b7b6..f1cf691 --- a/ansicon/util.c +++ b/ansicon/util.c @@ -1,128 +1,128 @@ -/* - util.c - Utility functions. -*/ - -#include "ansicon.h" -#include "version.h" - - -TCHAR prog_path[MAX_PATH]; -LPTSTR prog; -int log_level; -char tempfile[MAX_PATH]; -DWORD pid; - - -// Get just the name of the program: "C:\path\program.exe" -> "program". -// Returns a pointer within program; it is modified to remove the extension. -LPTSTR get_program_name( LPTSTR program ) -{ - LPTSTR name, ext; - - if (program == NULL) - { - GetModuleFileName( NULL, prog_path, lenof(prog_path) ); - program = prog_path; - } - name = wcsrchr( program, '\\' ); - if (name != NULL) - ++name; - else - name = program; - ext = wcsrchr( name, '.' ); - if (ext != NULL && ext != name) - *ext = '\0'; - - return name; -} - - -void DEBUGSTR( int level, LPTSTR szFormat, ... ) -{ - TCHAR szBuffer[1024], szEscape[1024]; - va_list pArgList; - HANDLE mutex; - DWORD wait; - FILE* file; - - if ((log_level & 3) < level && !(level & 4 & log_level)) - return; - - if (*tempfile == '\0') - { - _snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) ); - pid = GetCurrentProcessId(); - } - if (szFormat == NULL) - { - file = fopen( tempfile, "wt" ); - if (file != NULL) - { - SYSTEMTIME now; - GetLocalTime( &now ); - fprintf( file, "ANSICON v" PVERSA " log (%d) started " - "%d-%.2d-%.2d %d:%.2d:%.2d\n", - log_level, - now.wYear, now.wMonth, now.wDay, - now.wHour, now.wMinute, now.wSecond ); - fclose( file ); - } - return; - } - - va_start( pArgList, szFormat ); - _vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList ); - va_end( pArgList ); - - szFormat = szBuffer; - if (*szFormat == '\33') - { - BOOL first = TRUE; - LPTSTR pos = szEscape; - while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4) - { - if (*szFormat < 32) - { - *pos++ = '\\'; - switch (*szFormat) - { - case '\a': *pos++ = 'a'; break; - case '\b': *pos++ = 'b'; break; - case '\t': *pos++ = 't'; break; - case '\r': *pos++ = 'r'; break; - case '\n': *pos++ = 'n'; break; - case 27 : *pos++ = 'e'; break; - default: - pos += _snwprintf( pos, 32, L"%.*o", - (szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1, - *szFormat ); - } - } - else - { - if (*szFormat == '"') - { - if (first) - first = FALSE; - else if (szFormat[1] != '\0') - *pos++ = '\\'; - } - *pos++ = *szFormat; - } - } - *pos = '\0'; - szFormat = szEscape; - } - - mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); - wait = WaitForSingleObject( mutex, 500 ); - file = fopen( tempfile, "at" ); // _fmode might be binary - if (file != NULL) - { - fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat ); - fclose( file ); - } - if (wait == WAIT_OBJECT_0) - ReleaseMutex( mutex ); - CloseHandle( mutex ); -} +/* + util.c - Utility functions. +*/ + +#include "ansicon.h" +#include "version.h" + + +TCHAR prog_path[MAX_PATH]; +LPTSTR prog; +int log_level; +char tempfile[MAX_PATH]; +DWORD pid; + + +// Get just the name of the program: "C:\path\program.exe" -> "program". +// Returns a pointer within program; it is modified to remove the extension. +LPTSTR get_program_name( LPTSTR program ) +{ + LPTSTR name, ext; + + if (program == NULL) + { + GetModuleFileName( NULL, prog_path, lenof(prog_path) ); + program = prog_path; + } + name = wcsrchr( program, '\\' ); + if (name != NULL) + ++name; + else + name = program; + ext = wcsrchr( name, '.' ); + if (ext != NULL && ext != name) + *ext = '\0'; + + return name; +} + + +void DEBUGSTR( int level, LPTSTR szFormat, ... ) +{ + TCHAR szBuffer[1024], szEscape[1024]; + va_list pArgList; + HANDLE mutex; + DWORD wait; + FILE* file; + + if ((log_level & 3) < level && !(level & 4 & log_level)) + return; + + if (*tempfile == '\0') + { + _snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) ); + pid = GetCurrentProcessId(); + } + if (szFormat == NULL) + { + file = fopen( tempfile, (log_level & 8) ? "at" : "wt" ); + if (file != NULL) + { + SYSTEMTIME now; + GetLocalTime( &now ); + fprintf( file, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d) started " + "%d-%.2d-%.2d %d:%.2d:%.2d\n", + log_level, + now.wYear, now.wMonth, now.wDay, + now.wHour, now.wMinute, now.wSecond ); + fclose( file ); + } + return; + } + + va_start( pArgList, szFormat ); + _vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList ); + va_end( pArgList ); + + szFormat = szBuffer; + if (*szFormat == '\33') + { + BOOL first = TRUE; + LPTSTR pos = szEscape; + while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4) + { + if (*szFormat < 32) + { + *pos++ = '\\'; + switch (*szFormat) + { + case '\a': *pos++ = 'a'; break; + case '\b': *pos++ = 'b'; break; + case '\t': *pos++ = 't'; break; + case '\r': *pos++ = 'r'; break; + case '\n': *pos++ = 'n'; break; + case 27 : *pos++ = 'e'; break; + default: + pos += _snwprintf( pos, 32, L"%.*o", + (szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1, + *szFormat ); + } + } + else + { + if (*szFormat == '"') + { + if (first) + first = FALSE; + else if (szFormat[1] != '\0') + *pos++ = '\\'; + } + *pos++ = *szFormat; + } + } + *pos = '\0'; + szFormat = szEscape; + } + + mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); + wait = WaitForSingleObject( mutex, 500 ); + file = fopen( tempfile, "at" ); // _fmode might be binary + if (file != NULL) + { + fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat ); + fclose( file ); + } + if (wait == WAIT_OBJECT_0) + ReleaseMutex( mutex ); + CloseHandle( mutex ); +} diff --git a/ansicon/version.h b/ansicon/version.h old mode 100755 new mode 100644 index ff76004..ec1ae85 --- a/ansicon/version.h +++ b/ansicon/version.h @@ -1,9 +1,19 @@ -/* - version.h - Version defines. -*/ - -#define PVERS L"1.53" // wide string -#define PVERSA "1.53" // ANSI string (windres 2.16.91 didn't like L) -#define PVERE L"153" // wide environment string -#define PVEREA "153" // ANSI environment string -#define PVERB 1,5,3,0 // binary (resource) +/* + version.h - Version defines. +*/ + +#define PVERS L"1.66" // wide string +#define PVERSA "1.66" // ANSI string (windres 2.16.91 didn't like L) +#define PVERE L"166" // wide environment string +#define PVEREA "166" // ANSI environment string +#define PVERB 1,6,6,0 // binary (resource) + +#ifdef _WIN64 +# define BITS L"64" +# define BITSA "64" +#else +# define BITS L"32" +# define BITSA "32" +#endif + +#define ANSIDLL L"ANSI" BITS L".dll" diff --git a/ansicon/wow64.h b/ansicon/wow64.h old mode 100755 new mode 100644 index 33e0444..9ab8f40 --- a/ansicon/wow64.h +++ b/ansicon/wow64.h @@ -1,88 +1,88 @@ -/* - wow64.h - Definitions for Wow64. - - Mingw64/TDM does not include these Wow64 definitions. -*/ - -#ifndef WOW64_H -#define WOW64_H - -#define WIN32_LEAN_AND_MEAN -#include - -#define WOW64_CONTEXT_i386 0x00010000 - -#define WOW64_CONTEXT_CONTROL (WOW64_CONTEXT_i386 | 0x00000001L) -#define WOW64_CONTEXT_INTEGER (WOW64_CONTEXT_i386 | 0x00000002L) -#define WOW64_CONTEXT_SEGMENTS (WOW64_CONTEXT_i386 | 0x00000004L) -#define WOW64_CONTEXT_FLOATING_POINT (WOW64_CONTEXT_i386 | 0x00000008L) -#define WOW64_CONTEXT_DEBUG_REGISTERS (WOW64_CONTEXT_i386 | 0x00000010L) -#define WOW64_CONTEXT_EXTENDED_REGISTERS (WOW64_CONTEXT_i386 | 0x00000020L) - -#define WOW64_CONTEXT_FULL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS) - -#define WOW64_CONTEXT_ALL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS | \ - WOW64_CONTEXT_FLOATING_POINT | WOW64_CONTEXT_DEBUG_REGISTERS | \ - WOW64_CONTEXT_EXTENDED_REGISTERS) - -#define WOW64_SIZE_OF_80387_REGISTERS 80 - -#define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512 - -typedef struct _WOW64_FLOATING_SAVE_AREA { - DWORD ControlWord; - DWORD StatusWord; - DWORD TagWord; - DWORD ErrorOffset; - DWORD ErrorSelector; - DWORD DataOffset; - DWORD DataSelector; - BYTE RegisterArea[WOW64_SIZE_OF_80387_REGISTERS]; - DWORD Cr0NpxState; -} WOW64_FLOATING_SAVE_AREA; - -typedef WOW64_FLOATING_SAVE_AREA *PWOW64_FLOATING_SAVE_AREA; - -typedef struct _WOW64_CONTEXT { - - DWORD ContextFlags; - - DWORD Dr0; - DWORD Dr1; - DWORD Dr2; - DWORD Dr3; - DWORD Dr6; - DWORD Dr7; - - WOW64_FLOATING_SAVE_AREA FloatSave; - - DWORD SegGs; - DWORD SegFs; - DWORD SegEs; - DWORD SegDs; - - DWORD Edi; - DWORD Esi; - DWORD Ebx; - DWORD Edx; - DWORD Ecx; - DWORD Eax; - - DWORD Ebp; - DWORD Eip; - DWORD SegCs; - DWORD EFlags; - DWORD Esp; - DWORD SegSs; - - BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION]; - -} WOW64_CONTEXT; - -typedef WOW64_CONTEXT *PWOW64_CONTEXT; - - -typedef BOOL (WINAPI *TWow64GetThreadContext)( HANDLE hThread, PWOW64_CONTEXT lpContext ); -typedef BOOL (WINAPI *TWow64SetThreadContext)( HANDLE hThread, CONST WOW64_CONTEXT *lpContext ); - -#endif +/* + wow64.h - Definitions for Wow64. + + The 2003 Platform SDK does not include these Wow64 definitions. +*/ + +#ifndef WOW64_H +#define WOW64_H + +#define WIN32_LEAN_AND_MEAN +#include + +#define WOW64_CONTEXT_i386 0x00010000 + +#define WOW64_CONTEXT_CONTROL (WOW64_CONTEXT_i386 | 0x00000001L) +#define WOW64_CONTEXT_INTEGER (WOW64_CONTEXT_i386 | 0x00000002L) +#define WOW64_CONTEXT_SEGMENTS (WOW64_CONTEXT_i386 | 0x00000004L) +#define WOW64_CONTEXT_FLOATING_POINT (WOW64_CONTEXT_i386 | 0x00000008L) +#define WOW64_CONTEXT_DEBUG_REGISTERS (WOW64_CONTEXT_i386 | 0x00000010L) +#define WOW64_CONTEXT_EXTENDED_REGISTERS (WOW64_CONTEXT_i386 | 0x00000020L) + +#define WOW64_CONTEXT_FULL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS) + +#define WOW64_CONTEXT_ALL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS | \ + WOW64_CONTEXT_FLOATING_POINT | WOW64_CONTEXT_DEBUG_REGISTERS | \ + WOW64_CONTEXT_EXTENDED_REGISTERS) + +#define WOW64_SIZE_OF_80387_REGISTERS 80 + +#define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512 + +typedef struct _WOW64_FLOATING_SAVE_AREA { + DWORD ControlWord; + DWORD StatusWord; + DWORD TagWord; + DWORD ErrorOffset; + DWORD ErrorSelector; + DWORD DataOffset; + DWORD DataSelector; + BYTE RegisterArea[WOW64_SIZE_OF_80387_REGISTERS]; + DWORD Cr0NpxState; +} WOW64_FLOATING_SAVE_AREA; + +typedef WOW64_FLOATING_SAVE_AREA *PWOW64_FLOATING_SAVE_AREA; + +typedef struct _WOW64_CONTEXT { + + DWORD ContextFlags; + + DWORD Dr0; + DWORD Dr1; + DWORD Dr2; + DWORD Dr3; + DWORD Dr6; + DWORD Dr7; + + WOW64_FLOATING_SAVE_AREA FloatSave; + + DWORD SegGs; + DWORD SegFs; + DWORD SegEs; + DWORD SegDs; + + DWORD Edi; + DWORD Esi; + DWORD Ebx; + DWORD Edx; + DWORD Ecx; + DWORD Eax; + + DWORD Ebp; + DWORD Eip; + DWORD SegCs; + DWORD EFlags; + DWORD Esp; + DWORD SegSs; + + BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION]; + +} WOW64_CONTEXT; + +typedef WOW64_CONTEXT *PWOW64_CONTEXT; + + +typedef BOOL (WINAPI *TWow64GetThreadContext)( HANDLE hThread, PWOW64_CONTEXT lpContext ); +typedef BOOL (WINAPI *TWow64SetThreadContext)( HANDLE hThread, CONST WOW64_CONTEXT *lpContext ); + +#endif diff --git a/ansicon/x86/ANSI32.dll b/ansicon/x86/ANSI32.dll new file mode 100644 index 0000000..1441cb1 Binary files /dev/null and b/ansicon/x86/ANSI32.dll differ diff --git a/ansicon/x86/ansicon.exe b/ansicon/x86/ansicon.exe new file mode 100644 index 0000000..2ac34e4 Binary files /dev/null and b/ansicon/x86/ansicon.exe differ diff --git a/package/build.windows b/package/build.windows index 9416f20..ade5b9d 100644 --- a/package/build.windows +++ b/package/build.windows @@ -17,15 +17,15 @@ build() echo "Make sdb..." make - echo "Make ancicon..." - cd "ansicon" - if [ "${TARGET_OS}" = "windows-32" ] - then - make ansicon32 - else - make ansicon32 - fi - cd .. +# echo "Make ancicon..." +# cd "ansicon" +# if [ "${TARGET_OS}" = "windows-32" ] +# then +# make ansicon32 +# else +# make ansicon32 +# fi +# cd .. } # install @@ -49,4 +49,4 @@ install() [ "$1" = "clean" ] && clean [ "$1" = "build" ] && build [ "$1" = "install" ] && install -exit 0 \ No newline at end of file +exit 0