diff --git a/ANSI.c b/ANSI.c index a57574e..858049c 100644 --- a/ANSI.c +++ b/ANSI.c @@ -57,26 +57,18 @@ v1.31, 13 & 19 November, 2010: fix multibyte conversion problems. - v1.32, 4 December, 2010: - test for lpNumberOfCharsWritten/lpNumberOfBytesWritten being NULL. + v1.32, 4 & 12 December, 2010: + test for lpNumberOfCharsWritten/lpNumberOfBytesWritten being NULL; + recognise DSR and xterm window title; + ignore sequences starting with \e[? & \e[>. */ -#ifndef UNICODE -# define UNICODE -#endif - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include +#include "ansicon.h" #include -#include "injdll.h" -#include "debugstr.h" - -#define lenof(array) (sizeof(array)/sizeof(*(array))) #define isdigit(c) ('0' <= (c) && (c) <= '9') + // ========== Global variables and constants // Macro for adding pointers/DWORDs together without C arithmetic interfering @@ -107,14 +99,18 @@ HMODULE hKernel; // Kernel32 module handle HINSTANCE hDllInstance; // Dll instance handle HANDLE hConOut; // handle to CONOUT$ -#define ESC '\x1B' // ESCape character +#define ESC '\x1B' // ESCape character +#define BEL '\x07' #define MAX_ARG 16 // max number of args in an escape sequence int state; // automata state -//TCHAR prefix; // escape sequence prefix ( '[' or '(' ); +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; // color constants @@ -384,6 +380,30 @@ void PushBuffer( TCHAR c ) } } +//----------------------------------------------------------------------------- +// 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 ); + + for (; *seq; ++seq) + { + 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.uChar.UnicodeChar = *seq; + in.Event.KeyEvent.dwControlKeyState = 0; + WriteConsoleInput( hStdIn, &in, 1, &out ); + } +} + // ========== Print functions //----------------------------------------------------------------------------- @@ -410,7 +430,11 @@ void InterpretEscSeq( void ) SMALL_RECT Rect; CHAR_INFO CharInfo; - //if (prefix == '[') + // Just ignore \e[? & \e[> sequences. + if (prefix2 != 0) + return; + + if (prefix == '[') { GetConsoleScreenBufferInfo( hConOut, &Info ); switch (suffix) @@ -707,13 +731,49 @@ void InterpretEscSeq( void ) SetConsoleCursorPosition( hConOut, SavePos ); return; + case 'n': // ESC[#n Device status report + if (es_argc != 1) return; // ESC[n == ESC[0n -> ignored + if (es_argv[0] == 5) + SendSequence( L"\x1b[0n" ); // "OK" + else if (es_argv[0] == 6) + { + TCHAR buf[32]; + wsprintf( buf, L"\x1b[%d;%dR", Info.dwCursorPosition.Y + 1, + Info.dwCursorPosition.X + 1 ); + SendSequence( buf ); + } + 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 == ']') + { + if (es_argc == 1 && es_argv[0] == 0) // ESC]0;titleST + { + DEBUGSTR( L"SetConsoleTitle = %d", SetConsoleTitle( Pt_arg ) ); + } + } } - //----------------------------------------------------------------------------- // ParseAndPrintString(hDev, lpBuffer, nNumberOfBytesToWrite) // Parses the string lpBuffer, interprets the escapes sequences and prints the @@ -748,11 +808,14 @@ ParseAndPrintString( HANDLE hDev, else if (state == 2) { if (*s == ESC) ; // \e\e...\e == \e - else if ((*s == '[')) // || (*s == '(')) + else if ((*s == '[') || (*s == ']')) // || (*s == '(')) { FlushBuffer(); - //prefix = *s; + prefix = *s; + prefix2 = 0; state = 3; + Pt_len = 0; + *Pt_arg = '\0'; } else state = 1; } @@ -771,6 +834,10 @@ ParseAndPrintString( HANDLE hDev, es_argv[1] = 0; state = 4; } + else if (*s == '?' || *s == '>') + { + prefix2 = *s; + } else { es_argc = 0; @@ -788,7 +855,9 @@ ParseAndPrintString( HANDLE hDev, else if (*s == ';') { if (es_argc < MAX_ARG-1) es_argc++; - es_argv[es_argc] = 0; + es_argv[es_argc] = 0; + if (prefix == ']') + state = 5; } else { @@ -798,6 +867,23 @@ ParseAndPrintString( HANDLE hDev, 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; + } } FlushBuffer(); if (lpNumberOfBytesWritten != NULL) @@ -834,11 +920,6 @@ void Inject( LPPROCESS_INFORMATION pinfo, LPPROCESS_INFORMATION lpi, InjectDLL32( pinfo, dll ); #endif } - else - { - DEBUGSTR( L" Unsupported process type" ); - } - if (lpi) memcpy( lpi, pinfo, sizeof(PROCESS_INFORMATION) ); @@ -1194,14 +1275,10 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) 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 ); + DEBUGSTR( NULL ); #endif hDllInstance = hInstance; // save Dll instance handle diff --git a/ansicon.c b/ansicon.c index 42da6a2..40ac604 100644 --- a/ansicon.c +++ b/ansicon.c @@ -43,30 +43,19 @@ VC compatibility (2008 Express for 32-bit, PSDK 2003 R2 for 64-bit); explicitly use wide characters (stick with TCHAR, but not ). - v1.32, 4 December, 2010: - make -p more robust. + v1.32, 4 & 12 December, 2010: + make -p more robust; + inject into GUI processes again. */ #define PVERS L"1.32" -#define PDATE L"4 December, 2010" +#define PDATE L"12 December, 2010" -#ifndef UNICODE -# define UNICODE -#endif - -#define WIN32_LEAN_AND_MEAN -#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread -#include -#include -#include +#include "ansicon.h" #include #include #include #include -#include "injdll.h" -#include "debugstr.h" - -#define lenof(array) (sizeof(array)/sizeof(*(array))) #ifdef __MINGW32__ int _CRT_glob = 0; diff --git a/ansicon.h b/ansicon.h new file mode 100644 index 0000000..2708621 --- /dev/null +++ b/ansicon.h @@ -0,0 +1,46 @@ +/* + ansicon.h - Header file for common definitions. + + Jason Hood, 12 December, 2010 (originally injdll.h, 20 June, 2009). +*/ + +#ifndef ANSICON_H +#define ANSICON_H + +#ifndef UNICODE +# define UNICODE +#endif + +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread +#include +#include +#include + +#define lenof(array) (sizeof(array)/sizeof(*(array))) + + +BOOL ProcessType( LPPROCESS_INFORMATION ); +void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR ); +void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR ); + + +// ========== Auxiliary debug function + +//#define MYDEBUG 1 // use OutputDebugString +#define MYDEBUG 2 // use %temp%\ansicon.log +#ifndef MYDEBUG +# define MYDEBUG 0 // no debugging +#endif + +#if (MYDEBUG > 0) + void DEBUGSTR( LPTSTR szFormat, ... ); +#else +# if defined(_MSC_VER) && _MSC_VER <= 1400 + void DEBUGSTR() { } +# else +# define DEBUGSTR(...) +# endif +#endif + +#endif diff --git a/debugstr.c b/debugstr.c index 244cbdb..5b25cfd 100644 --- a/debugstr.c +++ b/debugstr.c @@ -2,24 +2,31 @@ debugstr.c - Auxiliary debug functionality. */ -#ifndef UNICODE -# define UNICODE +#include "ansicon.h" + +#if (MYDEBUG > 1) +char tempfile[MAX_PATH]; #endif -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include "debugstr.h" - -#define lenof(array) (sizeof(array)/sizeof(*(array))) - #if (MYDEBUG > 0) void DEBUGSTR( LPTSTR szFormat, ... ) // sort of OutputDebugStringf { TCHAR szBuffer[1024], szEscape[1024]; va_list pArgList; + +#if (MYDEBUG > 1) + if (*tempfile == '\0') + _snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) ); +#endif + + if (szFormat == NULL) + { +#if (MYDEBUG > 1) + DeleteFileA( tempfile ); +#endif + return; + } + va_start( pArgList, szFormat ); _vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList ); va_end( pArgList ); @@ -33,33 +40,34 @@ void DEBUGSTR( LPTSTR szFormat, ... ) // sort of OutputDebugStringf { 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 27 : *pos++ = 'e'; break; - default: - pos += wprintf( pos, L"%.*o", - (szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1, - *szFormat ); - } + *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') - ; - else - *pos++ = '\\'; - *pos++ = '"'; + if (first) + first = FALSE; + else if (szFormat[1] == '\0') + ; + else + *pos++ = '\\'; + *pos++ = '"'; } else { - *pos++ = *szFormat; + *pos++ = *szFormat; } } *pos = '\0'; diff --git a/injdll32.c b/injdll32.c index f7dbff3..d2f7df1 100644 --- a/injdll32.c +++ b/injdll32.c @@ -15,7 +15,7 @@ I've decided to simply keep the memory. */ -#include "injdll.h" +#include "ansicon.h" #ifdef _WIN64 #if defined(__MINGW64__) || (defined(_MSC_VER) && _MSC_VER <= 1400) @@ -65,7 +65,10 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) GETPROC( Wow64SetThreadContext ); // Assume if one is defined, so is the other. if (Wow64GetThreadContext == 0) + { + DEBUGSTR( "Failed to get pointer to Wow64GetThreadContext.\n" ); return; + } #endif STARTUPINFO si; @@ -78,7 +81,10 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) CopyMemory( code + len - 7*sizeof(TCHAR), L"-LLW.exe", 9*sizeof(TCHAR) ); if (!CreateProcess( (LPCTSTR)code, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi )) + { + DEBUGSTR( "Failed to execute \"%s\".\n", (LPCTSTR)code ); return; + } WaitForSingleObject( pi.hProcess, INFINITE ); GetExitCodeProcess( pi.hProcess, &LLW ); CloseHandle( pi.hProcess ); diff --git a/injdll64.c b/injdll64.c index b6f8729..8b987b0 100644 --- a/injdll64.c +++ b/injdll64.c @@ -15,7 +15,7 @@ I've decided to simply keep the memory. */ -#include "injdll.h" +#include "ansicon.h" void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll ) { diff --git a/makefile b/makefile index 6fbefc2..855f9c8 100644 --- a/makefile +++ b/makefile @@ -11,6 +11,9 @@ CC = gcc CFLAGS = -O2 -Wall +X86OBJS = x86/proctype.o x86/injdll32.o x86/debugstr.o +X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/debugstr.o + x86/%.o: %.c $(CC) -m32 -c $(CFLAGS) $(CPPFLAGS) $< -o $@ @@ -32,19 +35,19 @@ ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll x64/ANSI-LLW.exe x86: mkdir x86 -x86/ansicon.exe: x86/ansicon.o x86/debugstr.o x86/proctype.o x86/injdll32.o x86/ansiconv.o +x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o $(CC) -m32 $+ -s -o $@ -x86/ANSI32.dll: x86/ANSI.o x86/debugstr.o x86/proctype.o x86/injdll32.o x86/ansiv.o +x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared x64: mkdir x64 -x64/ansicon.exe: x64/ansicon.o x64/debugstr.o x64/proctype.o x64/injdll64.o x64/injdll32.o x64/ansiconv.o +x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o $(CC) -m64 $+ -s -o $@ -x64/ANSI64.dll: x64/ANSI.o x64/debugstr.o x64/proctype.o x64/injdll64.o x64/injdll32.o x64/ansiv.o +x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o $(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared x64/ANSI32.dll: x86/ANSI32.dll @@ -58,6 +61,13 @@ x86/ansiv.o: ansi.rc x64/ansiconv.o: ansicon.rc x64/ansiv.o: ansi.rc +ansicon.c: ansicon.h +ANSI.c: ansicon.h +debugstr.c: ansicon.h +injdll32.c: ansicon.h +injdll64.c: ansicon.h +proctype.c: ansicon.h + clean: -rm x86/*.o -rm x64/*.o diff --git a/makefile.vc b/makefile.vc index 3b1413d..72a1706 100644 --- a/makefile.vc +++ b/makefile.vc @@ -28,6 +28,9 @@ CC = cl CFLAGS = /nologo /W3 /Ox /GF /D_CRT_SECURE_NO_WARNINGS LIBS = advapi32.lib shell32.lib user32.lib +X86OBJS = x86\proctype.obj x86\injdll32.obj x86\debugstr.obj +X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\debugstr.obj + {}.c{$(DIR)}.obj: $(CC) /c $(CFLAGS) /Fo$@ $< @@ -43,10 +46,10 @@ ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll x64\ANSI32.dll x64\ANSI-LLW.exe x86: mkdir x86 -x86\ansicon.exe: x86\ansicon.obj x86\proctype.obj x86\injdll32.obj x86\ansicon.res +x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res $(CC) /nologo /Fe$@ $** $(LIBS) -x86\ANSI32.dll: x86\ANSI.obj x86\proctype.obj x86\injdll32.obj x86\ansi.res +x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res !IF $(BITS) == 32 $(CC) /nologo /LD /Fe$@ $** $(LIBS) !ENDIF @@ -54,10 +57,10 @@ x86\ANSI32.dll: x86\ANSI.obj x86\proctype.obj x86\injdll32.obj x86\ansi.res x64: mkdir x64 -x64\ansicon.exe: x64\ansicon.obj x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\ansicon.res +x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res $(CC) /nologo /Fe$@ $** $(LIBS) bufferoverflowu.lib -x64\ANSI64.dll: x64\ANSI.obj x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\ansi.res +x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res $(CC) /nologo /LD /Fe$@ $** $(LIBS) bufferoverflowu.lib x64\ANSI32.dll: x86\ANSI32.dll @@ -66,5 +69,12 @@ x64\ANSI32.dll: x86\ANSI32.dll x64\ANSI-LLW.exe: ANSI-LLW.c $(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? bufferoverflowu.lib +ansicon.c: ansicon.h +ANSI.c: ansicon.h +debugstr.c: ansicon.h +injdll32.c: ansicon.h +injdll64.c: ansicon.h +proctype.c: ansicon.h + clean: - -del $(DIR)\*.obj $(DIR)\*.res + -del $(DIR)\*.obj $(DIR)\*.res $(DIR)\*.lib $(DIR)\*.exp diff --git a/proctype.c b/proctype.c index cf84f7f..346f2f2 100644 --- a/proctype.c +++ b/proctype.c @@ -2,15 +2,13 @@ Test for a valid process. */ -#define WIN32_LEAN_AND_MEAN -#include +#include "ansicon.h" int ProcessType( LPPROCESS_INFORMATION pinfo ) { MEMORY_BASIC_INFORMATION minfo; char* ptr = 0; - int type = 0; while (VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) )) { @@ -28,37 +26,31 @@ int ProcessType( LPPROCESS_INFORMATION pinfo ) { if (nt_header.Signature == IMAGE_NT_SIGNATURE) { - if (nt_header.OptionalHeader.Subsystem == - IMAGE_SUBSYSTEM_WINDOWS_CUI) + if (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || + nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) - { - type = 32; + return 32; #ifdef _WIN64 - } - else if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) - { - type = 64; + if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) + return 64; #endif - } - else - { - //DEBUGSTR( L" Ignoring unsupported machine (%x)", - // nt_header.FileHeader.Machine ); - } + DEBUGSTR( L" Ignoring unsupported machine (0x%X)", + nt_header.FileHeader.Machine ); } else { - //DEBUGSTR( L" Ignoring non-console subsystem (%u)", - // nt_header.OptionalHeader.Subsystem ); + DEBUGSTR( L" Ignoring non-Windows subsystem (%u)", + nt_header.OptionalHeader.Subsystem ); } - break; } } + return 0; } } ptr += minfo.RegionSize; } - return type; + DEBUGSTR( L" Ignoring non-Windows process" ); + return 0; } diff --git a/readme.txt b/readme.txt index a7e2c5c..a877998 100644 --- a/readme.txt +++ b/readme.txt @@ -114,6 +114,9 @@ \e[#@ ICH: Insert CHaracter \e[#P DCH: Delete CHaracter \e[#;#;#m SGM: Set Graphics Mode + \e[#n DSR: Device Status Report + \e[21t Report (xterm) window's title + \e]0;titleBEL Set (xterm) window's title (and icon) `\e' represents the escape character (ASCII 27); `#' represents a decimal number (optional, in most cases defaulting to 1). Regarding @@ -144,9 +147,11 @@ Legend: + added, - bug-fixed, * changed. - 1.32 - 4 December, 2010: + 1.32 - 12 December, 2010: - fixed crash due to NULL lpNumberOfBytesWritten/lpNumberOfCharsWritten; - - -p will test the parent process for validity. + - -p will test the parent process for validity; + * hook into GUI processes; + + recognise DSR and xterm window title sequences. 1.31 - 19 November, 2010: - fixed multibyte support (no extra junk with UTF-8 files); @@ -238,6 +243,8 @@ 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. + ======= Contact @@ -245,6 +252,7 @@ mailto:jadoxa@yahoo.com.au http://ansicon.adoxa.cjb.net/ + https://github.com/adoxa/ansicon Jason Hood 11 Buckle Street @@ -264,5 +272,5 @@ in the version text and a source diff is included. - ============================= - Jason Hood, 4 December, 2010. + ============================== + Jason Hood, 12 December, 2010.