summaryrefslogtreecommitdiff
path: root/ansicon/ANSI.c
diff options
context:
space:
mode:
Diffstat (limited to 'ansicon/ANSI.c')
-rwxr-xr-xansicon/ANSI.c1825
1 files changed, 1825 insertions, 0 deletions
diff --git a/ansicon/ANSI.c b/ansicon/ANSI.c
new file mode 100755
index 0000000..3907252
--- /dev/null
+++ b/ansicon/ANSI.c
@@ -0,0 +1,1825 @@
+/*
+ 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 <tlhelp32.h>
+
+#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;
+}