commit a551a6e6c8a69b2fd2387ca279b72535013e5eff Author: Jason Hood Date: Mon Nov 8 15:31:01 2010 +1000 Initial commit (v1.30) diff --git a/ANSI-LLA.c b/ANSI-LLA.c new file mode 100644 index 0000000..8f900b9 --- /dev/null +++ b/ANSI-LLA.c @@ -0,0 +1,17 @@ +/* + ANSI-LLA.c - Output the 32-bit address of LoadLibraryA. + + Jason Hood, 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. +*/ + +#define WIN32_LEAN_AND_MEAN +#include + +int main( void ) +{ + return (DWORD)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), + "LoadLibraryA" ); +} diff --git a/ANSI.c b/ANSI.c new file mode 100644 index 0000000..ee7265f --- /dev/null +++ b/ANSI.c @@ -0,0 +1,1306 @@ +/* + 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. +*/ + +#define UNICODE +#define _UNICODE +#include + +#define lenof(str) (sizeof(str)/sizeof(TCHAR)) + +#include +#include +#include +#include +#include "injdll.h" + +#define isdigit(c) ('0' <= (c) && (c) <= '9') + +// ========== Auxiliary debug function + +#define MYDEBUG 0 // no debugging +//#define MYDEBUG 1 // use OutputDebugString +//#define MYDEBUG 2 // use %temp%\ansicon.log + +#if (MYDEBUG > 0) +#if (MYDEBUG > 1) +char tempfile[MAX_PATH]; +#endif +void DEBUGSTR( LPTSTR szFormat, ... ) // sort of OutputDebugStringf +{ + TCHAR szBuffer[1024], szEscape[1024]; + va_list pArgList; + va_start( pArgList, szFormat ); + _vsntprintf( szBuffer, lenof(szBuffer), szFormat, pArgList ); + va_end( pArgList ); + + szFormat = szBuffer; + if (*szFormat == '\\') + { + BOOL first = TRUE; + LPTSTR pos = szEscape; + while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4) + { + if (*szFormat < 32) + { + *pos++ = '\\'; + switch (*szFormat) + { + case '\b': *pos++ = 'b'; break; + case '\t': *pos++ = 't'; break; + case '\r': *pos++ = 'r'; break; + case '\n': *pos++ = 'n'; break; + case '\e': *pos++ = 'e'; break; + default: pos += _tprintf( pos, TEXT("%.*o"), + (szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1, + *szFormat ); + } + } + else if (*szFormat == '"') + { + if (first) + first = FALSE; + else if (szFormat[1] == '\0') + ; + else + *pos++ = '\\'; + *pos++ = '"'; + } + else + { + *pos++ = *szFormat; + } + } + *pos = '\0'; + szFormat = szEscape; + } +#if (MYDEBUG > 1) + FILE* file = fopen( tempfile, "a" ); + if (file != NULL) + { + _ftprintf( file, TEXT("%s\n"), szFormat ); + fclose( file ); + } +#else + OutputDebugString( szFormat ); +#endif +} +#else +#define DEBUGSTR(...) +#endif + +// ========== Global variables and constants + +// Macro for adding pointers/DWORDs together without C arithmetic interfering +#define MakePtr( cast, ptr, addValue ) (cast)((DWORD_PTR)(ptr)+(DWORD)(addValue)) + + +const char APIKernel[] = "kernel32.dll"; +const char APIKernelBase[] = "kernelbase.dll"; +const char APIConsole[] = "API-MS-Win-Core-Console-L1-1-0.dll"; +const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-L1-1-0.dll"; +const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-L1-1-0.dll"; +const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-L1-1-0.dll"; +const char APIFile[] = "API-MS-Win-Core-File-L1-1-0.dll"; + +PCSTR APIs[] = +{ + APIKernel, + APIConsole, + APIProcessThreads, + APIProcessEnvironment, + APILibraryLoader, + APIFile, + NULL +}; + + +HMODULE hKernel; // Kernel32 module handle +HINSTANCE hDllInstance; // Dll instance handle +HANDLE hConOut; // handle to CONOUT$ + +#define ESC '\x1B' // ESCape character + +#define MAX_ARG 16 // max number of args in an escape sequence +int state; // automata state +//TCHAR prefix; // escape sequence prefix ( '[' or '(' ); +TCHAR suffix; // escape sequence suffix +int es_argc; // escape sequence args count +int es_argv[MAX_ARG]; // escape sequence args + +// 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 + +WORD 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 +}; + +WORD 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 +}; + + +// screen attributes +WORD org_fg, org_bg, org_bold, org_ul; // original attributes +WORD foreground; +WORD background; +WORD bold; +WORD underline; +WORD rvideo = 0; +WORD concealed = 0; + +// saved cursor position +COORD SavePos = { 0, 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. + +typedef struct +{ + PCSTR lib; + PSTR name; + PROC newfunc; + PROC oldfunc; + PROC apifunc; +} HookFn, *PHookFn; + +HookFn Hooks[]; + +//----------------------------------------------------------------------------- +// 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( TEXT("error: %s(%d)"), TEXT(__FILE__), __LINE__ ); + return FALSE; + } + + // The MZ header has a pointer to the PE header + pNTHeader = MakePtr( PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew ); + + // One more test to make sure we're looking at a "PE" image + if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) + { + DEBUGSTR( TEXT("error: %s(%d)"), TEXT(__FILE__), __LINE__ ); + return FALSE; + } + + // We now have a valid pointer to the module's PE header. + // Get a pointer to its imports section. + pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR, + pDosHeader, + 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++) + { + PCSTR* lib; + PSTR pszModName = MakePtr( PSTR, pDosHeader, pImportDesc->Name ); + for (lib = APIs; *lib; ++lib) + if (stricmp( pszModName, *lib ) == 0) + break; + if (*lib == NULL) + continue; + + // Get a pointer to the found module's import address table (IAT). + pThunk = MakePtr( PIMAGE_THUNK_DATA, pDosHeader, 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 = (lib == APIs) ? 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( TEXT(" %hs"), 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( TEXT("error: %s(%d)"), TEXT(__FILE__), __LINE__ ); + 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( TEXT("error: %s(%d)"), TEXT(__FILE__), __LINE__ ); + 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) + { + DEBUGSTR( (restore) ? TEXT("Unhooking from %s") : TEXT("Hooking in %s"), + me.szModule ); + // Hook this function in this module. + if (!HookAPIOneMod( me.hModule, Hooks, restore )) + { + CloseHandle( hModuleSnap ); + return FALSE; + } + } + } + CloseHandle( hModuleSnap ); + return TRUE; +} + +// ========== Print Buffer functions + +#define BUFFER_SIZE 256 + +int nCharInBuffer = 0; +TCHAR 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( char c ) +// Adds a character in the buffer and flushes the buffer if it is full. +//----------------------------------------------------------------------------- + +void PushBuffer( TCHAR c ) +{ + ChBuffer[nCharInBuffer++] = c; + if (nCharInBuffer >= BUFFER_SIZE) + { + FlushBuffer(); + DEBUGSTR( TEXT("flush") ); + } +} + +// ========== 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; + DWORD len, NumberOfCharsWritten; + COORD Pos; + SMALL_RECT Rect; + CHAR_INFO CharInfo; + + //if (prefix == '[') + { + GetConsoleScreenBufferInfo( hConOut, &Info ); + switch (suffix) + { + case 'm': + if (es_argc == 0) es_argv[es_argc++] = 0; + for (i = 0; i < es_argc; i++) + { + switch (es_argv[i]) + { + case 0: + foreground = org_fg; + background = org_bg; + bold = (es_argc == 1) ? org_bold : 0; + underline = (es_argc == 1) ? org_ul : 0; + rvideo = 0; + concealed = 0; + break; + case 1: bold = FOREGROUND_INTENSITY; break; + case 5: /* blink */ + case 4: underline = BACKGROUND_INTENSITY; break; + case 7: rvideo = 1; break; + case 8: concealed = 1; break; + case 21: bold = 0; break; + case 25: + case 24: underline = 0; break; + case 27: rvideo = 0; break; + case 28: concealed = 0; break; + } + if (30 <= es_argv[i] && es_argv[i] <= 37) foreground = es_argv[i]-30; + if (40 <= es_argv[i] && es_argv[i] <= 47) background = es_argv[i]-40; + } + if (concealed) + { + if (rvideo) + { + attribut = foregroundcolor[foreground] + | backgroundcolor[foreground]; + if (bold) + attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } + else + { + attribut = foregroundcolor[background] + | backgroundcolor[background]; + if (underline) + attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY; + } + } + else if (rvideo) + { + attribut = foregroundcolor[background] | backgroundcolor[foreground]; + if (bold) + attribut |= BACKGROUND_INTENSITY; + if (underline) + attribut |= FOREGROUND_INTENSITY; + } + else + attribut = foregroundcolor[foreground] | backgroundcolor[background] + | bold | underline; + 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 '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 '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 '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 '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 '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 '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 '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; + + default: + return; + } + } +} + + +//----------------------------------------------------------------------------- +// 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; + LPTSTR s; + + if (hDev != hConOut) // reinit if device has changed + { + hConOut = hDev; + state = 1; + } + for (i = nNumberOfBytesToWrite, s = (LPTSTR)lpBuffer; i > 0; i--, s++) + { + if (state == 1) + { + if (*s == ESC) state = 2; + else PushBuffer( *s ); + } + else if (state == 2) + { + if (*s == ESC) ; // \e\e...\e == \e + else if ((*s == '[')) // || (*s == '(')) + { + FlushBuffer(); + //prefix = *s; + state = 3; + } + 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 + { + 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; + } + else + { + es_argc++; + suffix = *s; + InterpretEscSeq(); + state = 1; + } + } + } + FlushBuffer(); + *lpNumberOfBytesWritten = nNumberOfBytesToWrite - i; + return( i == 0 ); +} + + +// ========== Child process injection + +// Inject code into the target process to load our DLL. +void Inject( LPPROCESS_INFORMATION pinfo, LPPROCESS_INFORMATION lpi, + DWORD dwCreationFlags ) +{ + char* ptr = 0; + MEMORY_BASIC_INFORMATION minfo; + BOOL con = FALSE; +#ifdef _WIN64 + BOOL x86 = FALSE; +#endif + + while (VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) )) + { + IMAGE_DOS_HEADER dos_header; + SIZE_T read; + if (ReadProcessMemory( pinfo->hProcess, minfo.AllocationBase, + &dos_header, sizeof(dos_header), &read )) + { + if (dos_header.e_magic == IMAGE_DOS_SIGNATURE) + { + IMAGE_NT_HEADERS nt_header; + if (ReadProcessMemory( pinfo->hProcess, (char*)minfo.AllocationBase + + dos_header.e_lfanew, &nt_header, + sizeof(nt_header), &read )) + { + if (nt_header.Signature == IMAGE_NT_SIGNATURE) + { + if (nt_header.OptionalHeader.Subsystem == + IMAGE_SUBSYSTEM_WINDOWS_CUI) + { + if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) + { + con = TRUE; +#ifdef _WIN64 + x86 = TRUE; + } + else if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) + { + con = TRUE; +#endif + } + else + { + DEBUGSTR( TEXT(" Ignoring unsupported machine (%x)"), + nt_header.FileHeader.Machine ); + } + } + else + { + DEBUGSTR( TEXT(" Ignoring non-console subsystem (%u)"), + nt_header.OptionalHeader.Subsystem ); + } + break; + } + } + } + } + ptr += minfo.RegionSize; + } + + if (con) + { + CHAR dll[MAX_PATH]; +#ifdef _WIN64 + DWORD len = GetModuleFileNameA( GetModuleHandleA( "ANSI64.dll" ), + dll, sizeof(dll) ); + if (x86) + { + dll[len-6] = '3'; + dll[len-5] = '2'; + InjectDLL32( pinfo, dll ); + } + else + { + InjectDLL64( pinfo, dll ); + } +#else + GetModuleFileNameA( GetModuleHandleA( "ANSI32.dll" ), dll, sizeof(dll) ); + InjectDLL32( pinfo, dll ); +#endif + } + + if (lpi) + memcpy( lpi, pinfo, sizeof(PROCESS_INFORMATION) ); + + if (!(dwCreationFlags & CREATE_SUSPENDED)) + ResumeThread( pinfo->hThread ); +} + + +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 pi; + + if (!CreateProcessA( lpApplicationName, + lpCommandLine, + lpThreadAttributes, + lpProcessAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi )) + return FALSE; + + DEBUGSTR( TEXT("CreateProcessA: \"%hs\", \"%hs\""), + (lpApplicationName == NULL) ? "" : lpApplicationName, + (lpCommandLine == NULL) ? "" : lpCommandLine ); + Inject( &pi, lpProcessInformation, dwCreationFlags ); + + 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 pi; + + if (!CreateProcessW( lpApplicationName, + lpCommandLine, + lpThreadAttributes, + lpProcessAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi )) + return FALSE; + + DEBUGSTR( TEXT("CreateProcessW: \"%ls\", \"%ls\""), + (lpApplicationName == NULL) ? L"" : lpApplicationName, + (lpCommandLine == NULL) ? L"" : lpCommandLine ); + Inject( &pi, lpProcessInformation, dwCreationFlags ); + + return TRUE; +} + + +HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName ) +{ + HMODULE hMod = LoadLibraryA( lpFileName ); + if (hMod && hMod != hKernel) + { + DEBUGSTR( TEXT("Hooking in %hs (LoadLibraryA)"), lpFileName ); + HookAPIOneMod( hMod, Hooks, FALSE ); + } + return hMod; +} + + +HMODULE WINAPI MyLoadLibraryW( LPCWSTR lpFileName ) +{ + HMODULE hMod = LoadLibraryW( lpFileName ); + if (hMod && hMod != hKernel) + { + DEBUGSTR( TEXT("Hooking in %ls (LoadLibraryW)"), lpFileName ); + HookAPIOneMod( hMod, Hooks, FALSE ); + } + return hMod; +} + + +HMODULE WINAPI MyLoadLibraryExA( LPCSTR lpFileName, HANDLE hFile, + DWORD dwFlags ) +{ + HMODULE hMod = LoadLibraryExA( lpFileName, hFile, dwFlags ); + if (hMod && hMod != hKernel && !(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) + { + DEBUGSTR( TEXT("Hooking in %hs (LoadLibraryExA)"), lpFileName ); + HookAPIOneMod( hMod, Hooks, FALSE ); + } + return hMod; +} + + +HMODULE WINAPI MyLoadLibraryExW( LPCWSTR lpFileName, HANDLE hFile, + DWORD dwFlags ) +{ + HMODULE hMod = LoadLibraryExW( lpFileName, hFile, dwFlags ); + if (hMod && hMod != hKernel && !(dwFlags & LOAD_LIBRARY_AS_DATAFILE)) + { + DEBUGSTR( TEXT("Hooking in %ls (LoadLibraryExW)"), lpFileName ); + HookAPIOneMod( hMod, Hooks, FALSE ); + } + 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; + #define BUF_SIZE 4096 + WCHAR buf[BUF_SIZE]; + 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( TEXT("\\WriteConsoleA: %lu \"%.*hs\""), nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); + *lpNumberOfCharsWritten = 0; + while (nNumberOfCharsToWrite) + { + len = (nNumberOfCharsToWrite > BUF_SIZE) ? BUF_SIZE + : nNumberOfCharsToWrite; + MultiByteToWideChar( cp, 0, lpBuffer, len, buf, len ); + rc = ParseAndPrintString( hCon, buf, len, &Mode ); + *lpNumberOfCharsWritten += Mode; + lpBuffer += len; + nNumberOfCharsToWrite -= len; + } + return rc; + } + else + { + 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( TEXT("\\WriteConsoleW: %lu \"%.*ls\""), nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer ); + return ParseAndPrintString( hCon, lpBuffer, + nNumberOfCharsToWrite, + lpNumberOfCharsWritten ); + } + else + { + 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( TEXT("\\WriteFile: %lu \"%.*hs\""), nNumberOfBytesToWrite, nNumberOfBytesToWrite, lpBuffer ); + return MyWriteConsoleA( hFile, lpBuffer, + nNumberOfBytesToWrite, + lpNumberOfBytesWritten, + lpOverlapped ); + } + else // here, WriteFile is the old function (this module is not hooked) + { + return WriteFile( hFile, lpBuffer, + nNumberOfBytesToWrite, + lpNumberOfBytesWritten, + lpOverlapped ); + } +} + + +// ========== Environment variable + +void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi ) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (pcsbi == NULL) + { + HANDLE hConOut; + hConOut = CreateFile( TEXT("CONOUT$"), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + GetConsoleScreenBufferInfo( hConOut, &csbi ); + CloseHandle( hConOut ); + pcsbi = &csbi; + } + + TCHAR buf[64]; + wsprintf( buf, TEXT("%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( TEXT("ANSICON"), buf ); +} + +DWORD +WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) +{ + if (lstrcmpiA( lpName, "ANSICON" ) == 0) + set_ansicon( NULL ); + return GetEnvironmentVariableA( lpName, lpBuffer, nSize ); +} + +DWORD +WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize ) +{ + if (lstrcmpiW( lpName, L"ANSICON" ) == 0) + set_ansicon( NULL ); + return GetEnvironmentVariableW( lpName, lpBuffer, nSize ); +} + + +// ========== Initialisation + +HookFn Hooks[] = { + { 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, "LoadLibraryA", (PROC)MyLoadLibraryA, NULL, NULL }, + { APILibraryLoader, "LoadLibraryW", (PROC)MyLoadLibraryW, 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 }, + { NULL, NULL, NULL, NULL } +}; + +//----------------------------------------------------------------------------- +// OriginalAttr() +// Determine the original attributes for use by \e[m. +//----------------------------------------------------------------------------- +void OriginalAttr( void ) +{ + static const char attr2ansi[8] = // map console attribute to ANSI number + { + 0, 4, 2, 6, 1, 5, 3, 7 + }; + HANDLE hConOut; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + hConOut = CreateFile( TEXT("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 ); + foreground = org_fg = attr2ansi[csbi.wAttributes & 7]; + background = org_bg = attr2ansi[(csbi.wAttributes >> 4) & 7]; + bold = org_bold = csbi.wAttributes & FOREGROUND_INTENSITY; + underline = org_ul = csbi.wAttributes & BACKGROUND_INTENSITY; + + 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; + HMODULE api; + PHookFn hook; + +#if (MYDEBUG > 1) + _snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) ); +#endif + + if (dwReason == DLL_PROCESS_ATTACH) + { +#if (MYDEBUG > 1) + DeleteFileA( tempfile ); +#endif + + hDllInstance = hInstance; // save Dll instance handle + DEBUGSTR( TEXT("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 ); + api = GetModuleHandleA( hook->lib ); + if (api) + hook->apifunc = GetProcAddress( api, hook->name ); + } + + bResult = HookAPIAllMod( Hooks, FALSE ); + OriginalAttr(); + DisableThreadLibraryCalls( hInstance ); + } + else if (dwReason == DLL_PROCESS_DETACH && lpReserved == NULL) + { + DEBUGSTR( TEXT("Unloading") ); + HookAPIAllMod( Hooks, TRUE ); + } + + return( bResult ); +} diff --git a/COPYING.MinGW-w64-runtime.txt b/COPYING.MinGW-w64-runtime.txt new file mode 100644 index 0000000..9544220 --- /dev/null +++ b/COPYING.MinGW-w64-runtime.txt @@ -0,0 +1,169 @@ +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. + + +=============================================================== +gdtoa: Converting between IEEE floating point numbers and ASCII +=============================================================== + +The author of this software is David M. Gay. + +Copyright (C) 1997, 1998, 1999, 2000, 2001 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + + * * * * * * * + +The author of this software is David M. Gay. + +Copyright (C) 2005 by David M. Gay +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that the copyright notice and this permission notice and warranty +disclaimer appear in supporting documentation, and that the name of +the author or any of his current or former employers not be used in +advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN +NO EVENT SHALL THE AUTHOR OR ANY OF HIS CURRENT OR FORMER EMPLOYERS BE +LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + + * * * * * * * + +The author of this software is David M. Gay. + +Copyright (C) 2004 by David M. Gay. +All Rights Reserved +Based on material in the rest of /netlib/fp/gdota.tar.gz, +which is copyright (C) 1998, 2000 by Lucent Technologies. + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + + +========================= +Parts of the math library +========================= + +Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + +Developed at SunSoft, a Sun Microsystems, Inc. business. +Permission to use, copy, modify, and distribute this +software is freely granted, provided that this notice +is preserved. + + * * * * * * * + +Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + +Developed at SunPro, a Sun Microsystems, Inc. business. +Permission to use, copy, modify, and distribute this +software is freely granted, provided that this notice +is preserved. + + * * * * * * * + +FIXME: Cephes math lib +Copyright (C) 1984-1998 Stephen L. Moshier + +It sounds vague, but as to be found at +, it gives an +impression that the author could be willing to give an explicit +permission to distribute those files e.g. under a BSD style license. So +probably there is no problem here, although it could be good to get a +permission from the author and then add a license into the Cephes files +in MinGW runtime. At least on follow-up it is marked that debian sees the +version a-like BSD one. As MinGW.org (where those cephes parts are coming +from) distributes them now over 6 years, it should be fine. diff --git a/ansi.rc b/ansi.rc new file mode 100644 index 0000000..843e45b --- /dev/null +++ b/ansi.rc @@ -0,0 +1,41 @@ +/* + ansi.rc - Version resource for ANSI{32,64}.dll. + + Jason Hood, 11 November, 2009. +*/ + +#include + +#ifdef _WIN64 +# define BITS "64" +#else +# define BITS "32" +#endif + +1 VERSIONINFO +FILEVERSION 1,3,0,0 +PRODUCTVERSION 1,3,0,0 +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", "1.30" + VALUE "InternalName", "ANSI" BITS + VALUE "LegalCopyright", "Freeware" + VALUE "OriginalFilename", "ANSI" BITS ".dll" + VALUE "ProductName", "ANSICON" + VALUE "ProductVersion", "1.30" + } + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04B0 + } +} diff --git a/ansicon.c b/ansicon.c new file mode 100644 index 0000000..28d513b --- /dev/null +++ b/ansicon.c @@ -0,0 +1,541 @@ +/* + 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. +*/ + +#define PVERS "1.30" +#define PDATE "7 September, 2010" + +#define UNICODE +#define _UNICODE + +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread +#include +#include +#include +#include +#include +#include +#include +#include +#include "injdll.h" + +#ifdef __MINGW32__ +int _CRT_glob = 0; +#endif + + +#ifdef _WIN64 +# define InjectDLL InjectDLL64 +# define BITS "64" +#else +# define InjectDLL InjectDLL32 +# define BITS "32" +#endif + + +#define CMDKEY TEXT("Software\\Microsoft\\Command Processor") +#define AUTORUN TEXT("AutoRun") + + +void help( void ); + +void display( LPCTSTR, BOOL ); +LPTSTR skip_spaces( LPTSTR ); +LPTSTR skip_arg( LPTSTR ); + +void process_autorun( TCHAR ); + +BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ); +BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi ); + + +// Find the name of the DLL and inject it. +void Inject( LPPROCESS_INFORMATION ppi ) +{ + DWORD len; + CHAR dll[MAX_PATH]; + + len = GetModuleFileNameA( NULL, dll, sizeof(dll) ); + while (dll[len-1] != '\\') + --len; + lstrcpyA( dll + len, "ANSI" BITS ".dll" ); + + InjectDLL( ppi, dll ); +} + + +static HANDLE hConOut; +static CONSOLE_SCREEN_BUFFER_INFO csbi; + +void get_original_attr( void ) +{ + hConOut = CreateFile( TEXT("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 _tmain( int argc, TCHAR* argv[] ) +int main( void ) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + TCHAR* cmd; + BOOL option; + BOOL opt_m; + BOOL installed; + HMODULE ansi; + int rc = 0; + + int argc; + LPWSTR* argv = CommandLineToArgvW( GetCommandLineW(), &argc ); + + if (argc > 1) + { + if (lstrcmp( argv[1], TEXT("--help") ) == 0 || + (argv[1][0] == '-' && (argv[1][1] == '?' || argv[1][1] == 'h')) || + (argv[1][0] == '/' && argv[1][1] == '?')) + { + help(); + return rc; + } + if (lstrcmp( argv[1], TEXT("--version") ) == 0) + { + _putts( TEXT("ANSICON (" BITS "-bit) version " PVERS " (" PDATE ").") ); + return rc; + } + } + + option = (argc > 1 && argv[1][0] == '-'); + if (option && (_totlower( argv[1][1] ) == 'i' || + _totlower( argv[1][1] ) == 'u')) + { + process_autorun( argv[1][1] ); + return rc; + } + + get_original_attr(); + + opt_m = FALSE; + if (option && argv[1][1] == 'm') + { + WORD attr = 7; + if (_istxdigit( argv[1][2] )) + { + attr = _istdigit( argv[1][2] ) ? argv[1][2] - '0' + : (argv[1][2] | 0x20) - 'a' + 10; + if (_istxdigit( argv[1][3])) + { + attr <<= 4; + attr |= _istdigit( argv[1][3] ) ? argv[1][3] - '0' + : (argv[1][3] | 0x20) - 'a' + 10; + } + } + SetConsoleTextAttribute( hConOut, attr ); + + opt_m = TRUE; + ++argv; + --argc; + option = (argc > 1 && argv[1][0] == '-'); + } + + installed = (GetEnvironmentVariable( TEXT("ANSICON"), NULL, 0 ) != 0); + + if (option && argv[1][1] == 'p') + { + // If it's already installed, there's no need to do anything. + if (installed) + ; + else if (GetParentProcessInfo( &pi )) + { + pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId ); + pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); + SuspendThread( pi.hThread ); + Inject( &pi ); + ResumeThread( pi.hThread ); + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + } + else + { + _putts( TEXT("ANSICON: could not obtain the parent process.") ); + rc = 1; + } + } + else + { + ansi = 0; + if (!installed) + ansi = LoadLibrary( TEXT("ANSI" BITS ".dll") ); + + if (option && (argv[1][1] == 't' || argv[1][1] == 'T')) + { + BOOL title = (argv[1][1] == 'T'); + if (argc == 2) + { + argv[2] = L"-"; + ++argc; + } + for (; argc > 2; ++argv, --argc) + { + if (title) + _tprintf( TEXT("==> %s <==\n"), argv[2] ); + display( argv[2], title ); + if (title) + _puttchar( '\n' ); + } + } + else + { + // Retrieve the original command line, skipping our name and the option. + cmd = skip_spaces( skip_arg( skip_spaces( GetCommandLine() ) ) ); + if (opt_m) + cmd = skip_spaces( skip_arg( cmd ) ); + + if (cmd[0] == '-' && (cmd[1] == 'e' || cmd[1] == 'E')) + { + _fputts( cmd + 3, stdout ); + if (cmd[1] == 'e') + _puttchar( '\n' ); + } + else if (!isatty( 0 ) && *cmd == '\0') + { + display( TEXT("-"), FALSE ); + } + else + { + if (*cmd == '\0') + { + cmd = _tgetenv( TEXT("ComSpec") ); + if (cmd == NULL) + cmd = TEXT("cmd"); + } + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + if (CreateProcess( NULL, cmd, NULL,NULL, TRUE, 0, NULL,NULL, &si, &pi )) + { + SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); + WaitForSingleObject( pi.hProcess, INFINITE ); + } + else + { + *skip_arg( cmd ) = '\0'; + _tprintf( TEXT("ANSICON: '%s' could not be executed.\n"), cmd ); + rc = 1; + } + } + } + + if (ansi) + FreeLibrary( ansi ); + } + + set_original_attr(); + return rc; +} + + +void print_error( LPCTSTR name, BOOL title ) +{ + LPTSTR errmsg; + + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, GetLastError(), 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ); + if (!title) + _tprintf( TEXT("ANSICON: %s: "), name ); + _fputts( errmsg, stdout ); + LocalFree( errmsg ); +} + + +// Display a file. +void display( LPCTSTR name, BOOL title ) +{ + // Handle the pipe differently. + if (*name == '-' && name[1] == '\0') + { + if (title) + _puttchar( '\n' ); + int c; + while ((c = getchar()) != EOF) + putchar( c ); + return; + } + + HANDLE file = CreateFile( name, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, 0, NULL ); + if (file == INVALID_HANDLE_VALUE) + { + print_error( name, title ); + return; + } + + LARGE_INTEGER size; + GetFileSizeEx( file, &size ); + if (size.QuadPart != 0) + { + HANDLE map = CreateFileMapping( file, NULL, PAGE_READONLY, 0, 0, NULL ); + if (map) + { + if (title) + _puttchar( '\n' ); + LARGE_INTEGER offset; + offset.QuadPart = 0; + do + { + DWORD len = (size.QuadPart > 65536) ? 65536 : size.LowPart; + LPVOID mem = MapViewOfFile( map, FILE_MAP_READ, offset.HighPart, + offset.LowPart, len ); + if (mem) + { + fwrite( mem, 1, len, stdout ); + UnmapViewOfFile( mem ); + } + else + { + print_error( name, title ); + break; + } + offset.QuadPart += len; + size.QuadPart -= len; + } while (size.QuadPart); + CloseHandle( map ); + } + else + print_error( name, title ); + } + CloseHandle( file ); +} + + +// Add or remove ANSICON to AutoRun. +void process_autorun( TCHAR cmd ) +{ + HKEY cmdkey; + TCHAR ansicon[MAX_PATH+8]; + LPTSTR autorun, ansirun; + DWORD len, type, exist; + BOOL inst; + + len = GetModuleFileName( NULL, ansicon+2, MAX_PATH ); + ansicon[0] = '&'; + ansicon[1] = ansicon[2+len] = '"'; + _tcscpy( ansicon + 3+len, L" -p" ); + len += 6; + + inst = (_totlower( cmd ) == 'i'); + RegCreateKeyEx( (_istlower( 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 ); + autorun = malloc( exist + len * sizeof(TCHAR) + sizeof(TCHAR) ); + // Let's assume there's sufficient memory. + if (exist > sizeof(TCHAR)) + { + exist += sizeof(TCHAR); + RegQueryValueEx( cmdkey, AUTORUN, NULL, &type, (PBYTE)autorun, &exist ); + ansirun = _tcsstr( autorun, ansicon+1 ); + if (inst) + { + if (!ansirun) + { + _tcscpy( (LPTSTR)((PBYTE)autorun + exist - sizeof(TCHAR)), ansicon ); + RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, + exist + len*sizeof(TCHAR) ); + } + } + else + { + if (ansirun) + { + if (ansirun == autorun && exist == len*sizeof(TCHAR)) + RegDeleteValue( cmdkey, AUTORUN ); + else + { + if (ansirun > autorun && ansirun[-1] == '&') + --ansirun; + else if (autorun[len-1] != '&') + --len; + memcpy( ansirun, ansirun + len, exist - len*sizeof(TCHAR) ); + RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, + exist - len*sizeof(TCHAR) ); + } + } + } + } + else if (inst) + { + RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)(ansicon+1), + len*sizeof(TCHAR) ); + } + + 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 ) +{ + HANDLE hSnap; + PROCESSENTRY32 pe; + THREADENTRY32 te; + DWORD id = GetCurrentProcessId(); + BOOL fOk; + + hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, id ); + + if (hSnap == INVALID_HANDLE_VALUE) + return FALSE; + + find_proc_id( hSnap, id, &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; + + return fOk; +} + + +// Return the first non-space character from cmd. +LPTSTR skip_spaces( LPTSTR cmd ) +{ + while ((*cmd == ' ' || *cmd == '\t') && *cmd != '\0') + ++cmd; + + return cmd; +} + + +// Return the end of the argument at cmd. +LPTSTR skip_arg( LPTSTR cmd ) +{ + while (*cmd != ' ' && *cmd != '\t' && *cmd != '\0') + { + if (*cmd == '"') + { + do + ++cmd; + while (*cmd != '"' && *cmd != '\0'); + if (*cmd == '\0') + --cmd; + } + ++cmd; + } + + return cmd; +} + + +void help( void ) +{ + _putts( TEXT( +"ANSICON by Jason Hood .\n" +"Version " PVERS " (" PDATE "). Freeware.\n" +"http://ansicon.adoxa.cjb.net/\n" +"\n" +#ifdef _WIN64 +"Process ANSI escape sequences in Windows console programs.\n" +#else +"Process ANSI escape sequences in Win32 console programs.\n" +#endif +"\n" +"ansicon -i|I | -u|U\n" +"ansicon [-m[]] [-p | -e|E string | -t|T [file(s)] | program [args]]\n" +"\n" +" -i\t\tinstall - add ANSICON to the AutoRun entry\n" +" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n" +" -I -U\t\tuse local machine instead of current user\n" +" -m\t\tuse grey on black (\"monochrome\") or as default color\n" +" -p\t\thook into the parent process\n" +" -e\t\techo string\n" +" -E\t\techo string, don't append newline\n" +" -t\t\tdisplay files (\"-\" for stdin), combined as a single stream\n" +" -T\t\tdisplay files, name first, blank line before and after\n" +" program\trun the specified program\n" +" nothing\trun a new command processor, or display stdin if redirected\n" +"\n" +" is one or two hexadecimal digits; please use \"COLOR /?\" for details." + ) ); +} diff --git a/ansicon.rc b/ansicon.rc new file mode 100644 index 0000000..0b88235 --- /dev/null +++ b/ansicon.rc @@ -0,0 +1,35 @@ +/* + ansicon.rc - Version resource for ansicon.exe. + + Jason Hood, 11 November, 2009. +*/ + +#include + +1 VERSIONINFO +FILEVERSION 1,3,0,0 +PRODUCTVERSION 1,3,0,0 +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", "1.30" + VALUE "InternalName", "ansicon" + VALUE "LegalCopyright", "Freeware" + VALUE "OriginalFilename", "ansicon.exe" + VALUE "ProductName", "ANSICON" + VALUE "ProductVersion", "1.30" + } + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04B0 + } +} diff --git a/injdll.h b/injdll.h new file mode 100644 index 0000000..8541941 --- /dev/null +++ b/injdll.h @@ -0,0 +1,16 @@ +/* + injdll.h - Simple header file for injecting the DLL. + + Jason Hood, 20 June, 2009. +*/ + +#ifndef INJDLL_H +#define INJDLL_H + +#define WIN32_LEAN_AND_MEAN +#include + +void InjectDLL32( LPPROCESS_INFORMATION, LPCSTR ); +void InjectDLL64( LPPROCESS_INFORMATION, LPCSTR ); + +#endif diff --git a/injdll32.c b/injdll32.c new file mode 100644 index 0000000..6a1f73b --- /dev/null +++ b/injdll32.c @@ -0,0 +1,112 @@ +/* + 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 "injdll.h" + +#ifdef _WIN64 +#include "wow64.h" + +TWow64GetThreadContext Wow64GetThreadContext; +TWow64SetThreadContext Wow64SetThreadContext; + +#define CONTEXT WOW64_CONTEXT +#undef CONTEXT_CONTROL +#define CONTEXT_CONTROL WOW64_CONTEXT_CONTROL +#define GetThreadContext Wow64GetThreadContext +#define SetThreadContext Wow64SetThreadContext +#endif + + +DWORD LLA; + + +void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCSTR dll ) +{ + CONTEXT context; + DWORD len; + LPVOID mem; + DWORD mem32; + #define CODESIZE 20 + BYTE code[CODESIZE+MAX_PATH]; + + len = lstrlenA( dll ) + 1; + if (len > MAX_PATH) + return; + + if (LLA == 0) + { +#ifdef _WIN64 + extern HMODULE hKernel; + #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) + return; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + CopyMemory( code, dll, len - 7 ); // ...ANSI32.dll\0 + CopyMemory( code + len - 7, "-LLA.exe", 9 ); // ...ANSI-LLA.exe\0 + if (!CreateProcess( (char*)code, NULL, NULL, NULL, FALSE, 0, NULL, NULL, + &si, &pi )) + return; + WaitForSingleObject( pi.hProcess, INFINITE ); + GetExitCodeProcess( pi.hProcess, &LLA ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); +#else + LLA = (DWORD)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), + "LoadLibraryA" ); +#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; + + union + { + PBYTE pB; + PDWORD pL; + } ip; + ip.pB = code; + + *ip.pB++ = 0x68; // push eip + *ip.pL++ = context.Eip; + *ip.pB++ = 0x9c; // pushf + *ip.pB++ = 0x60; // pusha + *ip.pB++ = 0x68; // push "path\to\ANSI32.dll" + *ip.pL++ = mem32 + CODESIZE; + *ip.pB++ = 0xe8; // call LoadLibraryA + *ip.pL++ = LLA - (mem32 + (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/injdll64.c b/injdll64.c new file mode 100644 index 0000000..0a04140 --- /dev/null +++ b/injdll64.c @@ -0,0 +1,98 @@ +/* + 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. +*/ + +#define WIN32_LEAN_AND_MEAN +#include + +void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCSTR dll ) +{ + CONTEXT context; + DWORD len; + LPVOID mem; + DWORD64 LLA; + #define CODESIZE 92 + static BYTE code[CODESIZE+MAX_PATH] = { + 0,0,0,0,0,0,0,0, // original rip + 0,0,0,0,0,0,0,0, // LoadLibraryA + 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, "path\to\ANSI.dll" + 0xFF,0x15,-49,-1,-1,-1, // call LoadLibraryA + 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 LLA, fwiw + }; + + len = lstrlenA( dll ) + 1; + if (len > 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 ); + LLA = (DWORD64)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), + "LoadLibraryA" ); + + union + { + PBYTE pB; + PDWORD64 pL; + } ip; + ip.pB = code; + + *ip.pL++ = context.Rip; + *ip.pL++ = LLA; + + WriteProcessMemory( ppi->hProcess, mem, code, len, NULL ); + FlushInstructionCache( ppi->hProcess, mem, len ); + context.Rip = (DWORD64)mem + 16; + SetThreadContext( ppi->hThread, &context ); +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..d8c4452 --- /dev/null +++ b/makefile @@ -0,0 +1,53 @@ +# Simple makefile for ANSICON. +# Jason Hood, 11 March, 2006. Updated 20 June, 2009. + +# I've used TDM64 (gcc 4.5.0), 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, but will of course fail on the 64-bit. + +CC = gcc +CFLAGS = -O2 -Wall + +x86/%.o: %.c + $(CC) -m32 -c $(CFLAGS) $(CPPFLAGS) $< -o $@ + +x86/%v.o: %.rc + windres -U _WIN64 -F pe-i386 $< $@ + +x64/%.o: %.c + $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ + +x64/%v.o: %.rc + windres $< $@ + +all: ansicon32 ansicon64 + +ansicon32: x86/ansicon.exe x86/ANSI32.dll + +ansicon64: x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll x64/ANSI-LLA.exe + +x86/ansicon.exe: x86/ansicon.o x86/injdll32.o x86/ansiconv.o + $(CC) -m32 $+ -s -o $@ + +x86/ANSI32.dll: x86/ANSI.o x86/injdll32.o x86/ansiv.o + $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared + +x64/ansicon.exe: x64/ansicon.o x64/injdll64.o x64/ansiconv.o + $(CC) $+ -s -o $@ + +x64/ANSI64.dll: x64/ANSI.o x64/injdll64.o x64/injdll32.o x64/ansiv.o + $(CC) $+ -s -o $@ -mdll -Wl,-shared + +x64/ANSI32.dll: x86/ANSI32.dll + cmd /c copy x86\ANSI32.dll x64\ANSI32.dll + +x64/ANSI-LLA.exe: ANSI-LLA.c + $(CC) -m32 $(CFLAGS) $< -s -o $@ + +x86/ansiconv.o: ansicon.rc +x86/ansiv.o: ansi.rc +x64/ansiconv.o: ansicon.rc +x64/ansiv.o: ansi.rc + +clean: + -cmd /c "del x86\*.o x64\*.o" diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..d9ab8b8 --- /dev/null +++ b/readme.txt @@ -0,0 +1,236 @@ + + ANSICON + + Copyright 2005-2010 Jason Hood + + Version 1.30. 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 again with `-U') to remove + the AutoRun 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. + + + ===== + Usage + ===== + + 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. Passing the option `-p' (case sensitive) will enable the parent + process to recognise escapes (i.e. the command shell used to run ANSI- + CON). Use `-m' to set the current (and default) attribute to grey on + black ("monochrome"), or the attribute following the `m' (please use + `COLOR /?' for attribute values). The option `-e' will echo the command + line - the character after the `e' is ignored, the remainder is display- + ed verbatim; use `-E' to prevent a newline being written. The option + `-t' will display each file (or standard input if none or it is "-"), as + though they are a single file; `-T' will display the file name (in be- + tween "==> " and " <=="), a blank line (or an error message), the file + and another blank line. Anything else 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. + + 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. + + + ========= + Sequences + ========= + + The following escape sequences are recognised. + + \e[#A CUU: CUrsor Up + \e[#B CUD: CUrsor Down + \e[#C CUF: CUrsor Forward + \e[#D CUB: CUrsor Backward + \e[#E CNL: Cursor Next Line + \e[#F CPL: Cursor Preceding Line + \e[#G CHA: Cursor Horizontal Absolute + \e[#;#H CUP: CUrsor Position + \e[#;#f HVP: Horizontal and Vertical Position + \e[s SCP: Save Cursor Position + \e[u RCP: Restore Cursor Position + \e[#J ED: Erase Display + \e[#K EL: Erase Line + \e[#L IL: Insert Lines + \e[#M DL: Delete Lines + \e[#@ ICH: Insert CHaracter + \e[#P DCH: Delete CHaracter + \e[#;#;#m SGM: Set Graphics Mode + + `\e' represents the escape character (ASCII 27); `#' represents a + decimal number (optional, in most cases defaulting to 1). Regarding + SGM: 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. + + + =========== + Limitations + =========== + + The entire console buffer is used, not just the visible window. + + If running CMD.EXE, its own COLOR will be the initial color. + + + =============== + Version History + =============== + + Legend: + added, - bug-fixed, * changed. + + 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. + + + ======= + Contact + ======= + + mailto:jadoxa@yahoo.com.au + http://ansicon.adoxa.cjb.net/ + + 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, 7 September, 2010. diff --git a/wow64.h b/wow64.h new file mode 100644 index 0000000..6b8a1d2 --- /dev/null +++ b/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 + +#define WOW64_CONTEXT_i386 0x00010000 + +#define WOW64_CONTEXT_CONTROL (WOW64_CONTEXT_i386 | 0x00000001L) +#define WOW64_CONTEXT_INTEGER (WOW64_CONTEXT_i386 | 0x00000002L) +#define WOW64_CONTEXT_SEGMENTS (WOW64_CONTEXT_i386 | 0x00000004L) +#define WOW64_CONTEXT_FLOATING_POINT (WOW64_CONTEXT_i386 | 0x00000008L) +#define WOW64_CONTEXT_DEBUG_REGISTERS (WOW64_CONTEXT_i386 | 0x00000010L) +#define WOW64_CONTEXT_EXTENDED_REGISTERS (WOW64_CONTEXT_i386 | 0x00000020L) + +#define WOW64_CONTEXT_FULL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS) + +#define WOW64_CONTEXT_ALL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS | \ + WOW64_CONTEXT_FLOATING_POINT | WOW64_CONTEXT_DEBUG_REGISTERS | \ + WOW64_CONTEXT_EXTENDED_REGISTERS) + +#define WOW64_SIZE_OF_80387_REGISTERS 80 + +#define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512 + +typedef struct _WOW64_FLOATING_SAVE_AREA { + DWORD ControlWord; + DWORD StatusWord; + DWORD TagWord; + DWORD ErrorOffset; + DWORD ErrorSelector; + DWORD DataOffset; + DWORD DataSelector; + BYTE RegisterArea[WOW64_SIZE_OF_80387_REGISTERS]; + DWORD Cr0NpxState; +} WOW64_FLOATING_SAVE_AREA; + +typedef WOW64_FLOATING_SAVE_AREA *PWOW64_FLOATING_SAVE_AREA; + +typedef struct _WOW64_CONTEXT { + + DWORD ContextFlags; + + DWORD Dr0; + DWORD Dr1; + DWORD Dr2; + DWORD Dr3; + DWORD Dr6; + DWORD Dr7; + + WOW64_FLOATING_SAVE_AREA FloatSave; + + DWORD SegGs; + DWORD SegFs; + DWORD SegEs; + DWORD SegDs; + + DWORD Edi; + DWORD Esi; + DWORD Ebx; + DWORD Edx; + DWORD Ecx; + DWORD Eax; + + DWORD Ebp; + DWORD Eip; + DWORD SegCs; + DWORD EFlags; + DWORD Esp; + DWORD SegSs; + + BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION]; + +} WOW64_CONTEXT; + +typedef WOW64_CONTEXT *PWOW64_CONTEXT; + + +typedef BOOL WINAPI (*TWow64GetThreadContext)( HANDLE hThread, PWOW64_CONTEXT lpContext ); +typedef BOOL WINAPI (*TWow64SetThreadContext)( HANDLE hThread, CONST WOW64_CONTEXT *lpContext ); + +#endif diff --git a/x64/ANSI-LLA.exe b/x64/ANSI-LLA.exe new file mode 100644 index 0000000..42afd66 Binary files /dev/null and b/x64/ANSI-LLA.exe differ diff --git a/x64/ANSI32.dll b/x64/ANSI32.dll new file mode 100644 index 0000000..b1cf2fd Binary files /dev/null and b/x64/ANSI32.dll differ diff --git a/x64/ANSI64.dll b/x64/ANSI64.dll new file mode 100644 index 0000000..955a5c0 Binary files /dev/null and b/x64/ANSI64.dll differ diff --git a/x64/ansicon.exe b/x64/ansicon.exe new file mode 100644 index 0000000..d01f8b3 Binary files /dev/null and b/x64/ansicon.exe differ diff --git a/x86/ANSI32.dll b/x86/ANSI32.dll new file mode 100644 index 0000000..b1cf2fd Binary files /dev/null and b/x86/ANSI32.dll differ diff --git a/x86/ansicon.exe b/x86/ansicon.exe new file mode 100644 index 0000000..ebc26c3 Binary files /dev/null and b/x86/ansicon.exe differ