diff options
author | HyungKyu Song <hk76.song@samsung.com> | 2013-02-16 00:41:57 +0900 |
---|---|---|
committer | HyungKyu Song <hk76.song@samsung.com> | 2013-02-16 00:41:57 +0900 |
commit | aa44aabb66da71d4380b74481f609f6daac2c269 (patch) | |
tree | d0ffbd64e7371bded3c7ffd472464774f908e941 /ansicon | |
parent | 041e6080ec2c7e6db643943434e7ddff49ffb68a (diff) | |
download | sdb-aa44aabb66da71d4380b74481f609f6daac2c269.tar.gz sdb-aa44aabb66da71d4380b74481f609f6daac2c269.tar.bz2 sdb-aa44aabb66da71d4380b74481f609f6daac2c269.zip |
Tizen 2.0 Releasesubmit/tizen_2.0/20130215.192027tizen_2.0
Diffstat (limited to 'ansicon')
-rwxr-xr-x | ansicon/.gitignore | 3 | ||||
-rwxr-xr-x | ansicon/ANSI-LLW.c | 22 | ||||
-rwxr-xr-x | ansicon/ANSI.c | 1825 | ||||
-rwxr-xr-x | ansicon/COPYING.MinGW-w64-runtime.txt | 240 | ||||
-rwxr-xr-x | ansicon/G1.bat | 18 | ||||
-rwxr-xr-x | ansicon/G1.txt | bin | 0 -> 404 bytes | |||
-rwxr-xr-x | ansicon/ansi.rc | 42 | ||||
-rwxr-xr-x | ansicon/ansicon.c | 686 | ||||
-rwxr-xr-x | ansicon/ansicon.h | 47 | ||||
-rwxr-xr-x | ansicon/ansicon.rc | 36 | ||||
-rwxr-xr-x | ansicon/injdll32.c | 123 | ||||
-rwxr-xr-x | ansicon/injdll64.c | 96 | ||||
-rwxr-xr-x | ansicon/makefile | 82 | ||||
-rwxr-xr-x | ansicon/makefile.vc | 88 | ||||
-rwxr-xr-x | ansicon/proctype.c | 105 | ||||
-rwxr-xr-x | ansicon/readme.txt | 439 | ||||
-rwxr-xr-x | ansicon/util.c | 128 | ||||
-rwxr-xr-x | ansicon/version.h | 9 | ||||
-rwxr-xr-x | ansicon/wow64.h | 88 |
19 files changed, 4077 insertions, 0 deletions
diff --git a/ansicon/.gitignore b/ansicon/.gitignore new file mode 100755 index 0000000..a45c74f --- /dev/null +++ b/ansicon/.gitignore @@ -0,0 +1,3 @@ +*.zip +/x86 +/x64 diff --git a/ansicon/ANSI-LLW.c b/ansicon/ANSI-LLW.c new file mode 100755 index 0000000..28991f0 --- /dev/null +++ b/ansicon/ANSI-LLW.c @@ -0,0 +1,22 @@ +/* + 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 <windows.h> + +int main( void ) +{ + return (DWORD)GetProcAddress( GetModuleHandle( "kernel32.dll" ), + "LoadLibraryW" ); +} 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; +} diff --git a/ansicon/COPYING.MinGW-w64-runtime.txt b/ansicon/COPYING.MinGW-w64-runtime.txt new file mode 100755 index 0000000..3d11e87 --- /dev/null +++ b/ansicon/COPYING.MinGW-w64-runtime.txt @@ -0,0 +1,240 @@ +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 <Todd.Miller@courtesan.com> + +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 +<http://lists.debian.org/debian-legal/2004/12/msg00295.html>, 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 new file mode 100755 index 0000000..37a6bb8 --- /dev/null +++ b/ansicon/G1.bat @@ -0,0 +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 { ^| } ~ diff --git a/ansicon/G1.txt b/ansicon/G1.txt Binary files differnew file mode 100755 index 0000000..a007b91 --- /dev/null +++ b/ansicon/G1.txt diff --git a/ansicon/ansi.rc b/ansicon/ansi.rc new file mode 100755 index 0000000..16a4585 --- /dev/null +++ b/ansicon/ansi.rc @@ -0,0 +1,42 @@ +/* + ansi.rc - Version resource for ANSI{32,64}.dll. + + Jason Hood, 11 November, 2009. +*/ + +#include <winver.h> +#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 + } +} diff --git a/ansicon/ansicon.c b/ansicon/ansicon.c new file mode 100755 index 0000000..2b87abe --- /dev/null +++ b/ansicon/ansicon.c @@ -0,0 +1,686 @@ +/* + 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 <tchar.h>). + + 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 <tlhelp32.h> +#include <ctype.h> +#include <io.h> + +#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 <jadoxa@yahoo.com.au>.\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<level>] [-i] [-I] [-u] [-U] [-m[<attr>]] [-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 <attr> 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"<attr> 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 new file mode 100755 index 0000000..8819186 --- /dev/null +++ b/ansicon/ansicon.h @@ -0,0 +1,47 @@ +/* + 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 <windows.h> +#include <stdio.h> +#include <stdlib.h> + +#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 new file mode 100755 index 0000000..09b03c1 --- /dev/null +++ b/ansicon/ansicon.rc @@ -0,0 +1,36 @@ +/* + ansicon.rc - Version resource for ansicon.exe. + + Jason Hood, 11 November, 2009. +*/ + +#include <winver.h> +#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 + } +} diff --git a/ansicon/injdll32.c b/ansicon/injdll32.c new file mode 100755 index 0000000..25ed7ae --- /dev/null +++ b/ansicon/injdll32.c @@ -0,0 +1,123 @@ +/* + 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 ); +} diff --git a/ansicon/injdll64.c b/ansicon/injdll64.c new file mode 100755 index 0000000..c46bd46 --- /dev/null +++ b/ansicon/injdll64.c @@ -0,0 +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 ); +} diff --git a/ansicon/makefile b/ansicon/makefile new file mode 100755 index 0000000..0007bee --- /dev/null +++ b/ansicon/makefile @@ -0,0 +1,82 @@ +# 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.vc b/ansicon/makefile.vc new file mode 100755 index 0000000..da71872 --- /dev/null +++ b/ansicon/makefile.vc @@ -0,0 +1,88 @@ +# 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 diff --git a/ansicon/proctype.c b/ansicon/proctype.c new file mode 100755 index 0000000..27399cd --- /dev/null +++ b/ansicon/proctype.c @@ -0,0 +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; +} diff --git a/ansicon/readme.txt b/ansicon/readme.txt new file mode 100755 index 0000000..78f00eb --- /dev/null +++ b/ansicon/readme.txt @@ -0,0 +1,439 @@ + + 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. diff --git a/ansicon/util.c b/ansicon/util.c new file mode 100755 index 0000000..461b7b6 --- /dev/null +++ b/ansicon/util.c @@ -0,0 +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 ); +} diff --git a/ansicon/version.h b/ansicon/version.h new file mode 100755 index 0000000..ff76004 --- /dev/null +++ b/ansicon/version.h @@ -0,0 +1,9 @@ +/* + 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) diff --git a/ansicon/wow64.h b/ansicon/wow64.h new file mode 100755 index 0000000..33e0444 --- /dev/null +++ b/ansicon/wow64.h @@ -0,0 +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 <windows.h> + +#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 |