
Normally the display uses the window height; adding an intermediate character of `+` will use the buffer height. Report cursor position will also output `+R` when used with `+n`.
942 lines
22 KiB
C
942 lines
22 KiB
C
/*
|
|
ANSICON.c - ANSI escape sequence console driver.
|
|
|
|
Jason Hood, 21 to 23 October, 2005.
|
|
|
|
Original injection code was derived from Console Manager by Sergey Oblomov
|
|
(hoopoepg). Use of FlushInstructionCache came from www.catch22.net.
|
|
Additional information came from "Process-wide API spying - an ultimate hack",
|
|
Anton Bassov's article in "The Code Project" (use of OpenThread).
|
|
|
|
v1.01, 11 & 12 March, 2006:
|
|
-m option to set "monochrome" (grey on black);
|
|
restore original color on exit.
|
|
|
|
v1.10, 22 February, 2009:
|
|
ignore Ctrl+C/Ctrl+Break.
|
|
|
|
v1.13, 21 & 27 March, 2009:
|
|
alternate injection method, to work with DEP;
|
|
use Unicode.
|
|
|
|
v1.20, 17 to 21 June, 2009:
|
|
use a combination of the two injection methods;
|
|
test if ANSICON is already installed;
|
|
added -e (and -E) option to echo the command line (without newline);
|
|
added -t (and -T) option to type (display) files (with file name).
|
|
|
|
v1.21, 23 September, 2009:
|
|
added -i (and -u) to add (remove) ANSICON to AutoRun.
|
|
|
|
v1.24, 6 & 7 January, 2010:
|
|
no arguments to -t, or using "-" for the name, will read from stdin;
|
|
fix -t and -e when ANSICON was already loaded.
|
|
|
|
v1.25, 22 July, 2010:
|
|
added -IU for HKLM.
|
|
|
|
v1.30, 3 August to 7 September, 2010:
|
|
x64 support.
|
|
|
|
v1.31, 13 & 15 November, 2010:
|
|
use LLW to fix potential Unicode path problems;
|
|
VC compatibility (2008 Express for 32-bit, PSDK 2003 R2 for 64-bit);
|
|
explicitly use wide characters (stick with TCHAR, but not <tchar.h>).
|
|
|
|
v1.32, 4 to 22 December, 2010:
|
|
make -p more robust;
|
|
inject into GUI processes;
|
|
-i implies -p.
|
|
|
|
v1.50, 7 to 14 December, 2011:
|
|
-u does not imply -p;
|
|
add the PID to the debugging output;
|
|
use ANSICON_VER to test if already installed;
|
|
always place first in AutoRun;
|
|
logging is always available, controlled by ANSICON_LOG environment variable;
|
|
only restore the original color after program/echo/type;
|
|
return program's exit code.
|
|
|
|
7 January, 2012:
|
|
fixed installing into a piped CMD.EXE;
|
|
added a log message indicating all imports have been processed.
|
|
|
|
v1.52, 10 April, 2012:
|
|
fixed running "cmd" if "ComSpec" is not defined;
|
|
pass process & thread identifiers on the command line (for x86->x64).
|
|
|
|
v1.60, 22 & 24 November, 2012:
|
|
set the code page to convert strings correctly;
|
|
expand wildcards for -t;
|
|
write the date if appending to the log.
|
|
|
|
v1.62, 18 July, 2013:
|
|
write the bits to the log;
|
|
test if creating the registry key fails (HKLM requires admin privileges).
|
|
|
|
v1.63, 25 July, 2013:
|
|
don't write the reset sequence if output is redirected.
|
|
|
|
v1.70, 31 January to 7 February, 2014:
|
|
restore the original (current, not default) attributes if using ansicon.exe
|
|
when it's already installed;
|
|
use ANSICON_DEF if defined and -m not given;
|
|
-e and -t will not output anything if the DLL could not load;
|
|
use Unicode output (_O_U16TEXT, for compilers/systems that support it);
|
|
log: 64-bit addresses get an underscore between the 8-digit groups;
|
|
add error codes to some message.
|
|
|
|
v1.80, 28 October, 2017:
|
|
write newline with _putws, not putwchar (fixes redirecting to CON).
|
|
*/
|
|
|
|
#define PDATE L"23 November, 2017"
|
|
|
|
#include "ansicon.h"
|
|
#include "version.h"
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#include <locale.h>
|
|
|
|
#ifndef _O_U16TEXT
|
|
#define _O_U16TEXT 0x20000
|
|
#endif
|
|
|
|
#ifdef __MINGW32__
|
|
int _CRT_glob = 0;
|
|
#endif
|
|
|
|
|
|
#define CMDKEY L"Software\\Microsoft\\Command Processor"
|
|
#define AUTORUN L"AutoRun"
|
|
|
|
|
|
void help( void );
|
|
|
|
void display( LPCTSTR, BOOL );
|
|
void print_error( LPCTSTR );
|
|
LPTSTR skip_spaces( LPTSTR );
|
|
void get_arg( LPTSTR, LPTSTR*, LPTSTR* );
|
|
void get_file( LPTSTR, LPTSTR*, LPTSTR* );
|
|
|
|
void process_autorun( TCHAR );
|
|
|
|
BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe );
|
|
BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR );
|
|
|
|
|
|
static HANDLE hConOut;
|
|
static WORD wAttr;
|
|
|
|
void get_original_attr( void )
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
|
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0, 0 );
|
|
GetConsoleScreenBufferInfo( hConOut, &csbi );
|
|
wAttr = csbi.wAttributes;
|
|
}
|
|
|
|
|
|
void set_original_attr( void )
|
|
{
|
|
SetConsoleTextAttribute( hConOut, wAttr );
|
|
CloseHandle( hConOut );
|
|
}
|
|
|
|
|
|
// The fputws function in MSVCRT.DLL (Windows 7 x64) is broken for Unicode
|
|
// output (it just writes the first character). VC6 & 7 don't support Unicode
|
|
// output at all (just converting to ANSI) and even when it is supported, it
|
|
// just writes single characters (as does _putws & fwprintf). So what the
|
|
// heck, DIY.
|
|
int my_fputws( const wchar_t* s, FILE* f )
|
|
{
|
|
if (_isatty( _fileno( f ) ))
|
|
{
|
|
DWORD written;
|
|
WriteConsole( hConOut, s, (DWORD)wcslen( s ), &written, NULL );
|
|
}
|
|
else
|
|
{
|
|
fputws( s, f );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define fputws my_fputws
|
|
#define _putws( s ) my_fputws( s L"\n", stdout )
|
|
|
|
|
|
HANDLE hHeap;
|
|
#if defined(_WIN64)
|
|
LPTSTR DllNameType;
|
|
#endif
|
|
|
|
// Find the name of the DLL and inject it.
|
|
BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
|
|
{
|
|
DWORD len;
|
|
int type;
|
|
PBYTE base;
|
|
|
|
#ifdef _WIN64
|
|
if (app != NULL)
|
|
#endif
|
|
DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId );
|
|
type = ProcessType( ppi, &base, gui );
|
|
if (type <= 0)
|
|
{
|
|
if (type == 0)
|
|
fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app );
|
|
return FALSE;
|
|
}
|
|
|
|
len = (DWORD)(prog - prog_path);
|
|
memcpy( DllName, prog_path, TSIZE(len) );
|
|
#ifdef _WIN64
|
|
wsprintf( DllName + len, L"ANSI%d.dll", (type == 48) ? 64 : type );
|
|
DllNameType = DllName + len + 4;
|
|
set_ansi_dll();
|
|
if (type == 64)
|
|
InjectDLL( ppi, base );
|
|
else if (type == 32)
|
|
InjectDLL32( ppi, base );
|
|
else // (type == 48)
|
|
InjectDLL64( ppi );
|
|
#else
|
|
wcscpy( DllName + len, L"ANSI32.dll" );
|
|
set_ansi_dll();
|
|
InjectDLL( ppi, base );
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Use CreateRemoteThread to load our DLL in the target process.
|
|
void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
|
|
{
|
|
HANDLE hSnap;
|
|
MODULEENTRY32 me;
|
|
PBYTE LLW;
|
|
BOOL fOk;
|
|
DWORD len;
|
|
LPVOID mem;
|
|
HANDLE thread;
|
|
DWORD ticks;
|
|
#ifdef _WIN64
|
|
BOOL WOW64;
|
|
int type;
|
|
#endif
|
|
|
|
DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId );
|
|
|
|
// Find the base address of kernel32.dll.
|
|
ticks = GetTickCount();
|
|
while ((hSnap = CreateToolhelp32Snapshot(
|
|
TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ppi->dwProcessId ))
|
|
== INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD err = GetLastError();
|
|
#ifndef _WIN64
|
|
if (err == ERROR_PARTIAL_COPY)
|
|
{
|
|
DEBUGSTR( 1, " Ignoring 64-bit process (use x64\\ansicon)" );
|
|
fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr );
|
|
return;
|
|
}
|
|
#endif
|
|
// I really don't think this would happen, but if it does, give up after
|
|
// two seconds to avoid a potentially infinite loop.
|
|
if (err == ERROR_BAD_LENGTH && GetTickCount() - ticks < 2000)
|
|
{
|
|
Sleep( 1 );
|
|
continue;
|
|
}
|
|
DEBUGSTR( 1, " Unable to create snapshot (%u)", err );
|
|
no_go:
|
|
fputws( L"ANSICON: unable to inject into parent.\n", stderr );
|
|
return;
|
|
}
|
|
LLW = NULL;
|
|
me.dwSize = sizeof(MODULEENTRY32);
|
|
for (fOk = Module32First( hSnap, &me ); fOk; fOk = Module32Next( hSnap, &me ))
|
|
{
|
|
if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0)
|
|
{
|
|
LLW = me.modBaseAddr;
|
|
break;
|
|
}
|
|
}
|
|
CloseHandle( hSnap );
|
|
if (LLW == NULL)
|
|
{
|
|
DEBUGSTR( 1, " Unable to locate kernel32.dll" );
|
|
goto no_go;
|
|
}
|
|
|
|
len = (DWORD)(prog - prog_path);
|
|
memcpy( DllName, prog_path, TSIZE(len) );
|
|
#ifdef _WIN64
|
|
type = (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) ? 32 : 64;
|
|
wsprintf( DllName + len, L"ANSI%d.dll", type );
|
|
LLW += GetProcRVA( L"kernel32.dll", "LoadLibraryW", type );
|
|
#else
|
|
wcscpy( DllName + len, L"ANSI32.dll" );
|
|
LLW += GetProcRVA( L"kernel32.dll", "LoadLibraryW" );
|
|
#endif
|
|
if (LLW == me.modBaseAddr)
|
|
goto no_go;
|
|
|
|
mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE );
|
|
if (mem == NULL)
|
|
{
|
|
DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError());
|
|
goto no_go;
|
|
}
|
|
WriteProcMem( mem, DllName, TSIZE(len + 11) );
|
|
thread = CreateRemoteThread( ppi->hProcess, NULL, 4096,
|
|
(LPTHREAD_START_ROUTINE)LLW, mem, 0, NULL );
|
|
WaitForSingleObject( thread, INFINITE );
|
|
CloseHandle( thread );
|
|
VirtualFreeEx( ppi->hProcess, mem, 0, MEM_RELEASE );
|
|
}
|
|
|
|
|
|
DWORD CtrlHandler( DWORD event )
|
|
{
|
|
return (event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT);
|
|
}
|
|
|
|
|
|
int main( void )
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
LPTSTR argv, arg, cmd;
|
|
TCHAR buf[4];
|
|
BOOL shell, run, gui;
|
|
HMODULE ansi;
|
|
DWORD len;
|
|
int rc = 0;
|
|
|
|
// Convert wide strings using the current code page.
|
|
sprintf( (LPSTR)buf, ".%u", GetConsoleOutputCP() );
|
|
setlocale( LC_CTYPE, (LPSTR)buf );
|
|
|
|
// Switch console output to Unicode.
|
|
if (_isatty( 1 ))
|
|
_setmode( 1, _O_U16TEXT);
|
|
if (_isatty( 2 ))
|
|
_setmode( 2, _O_U16TEXT);
|
|
|
|
// Create a console handle and store the current attributes.
|
|
get_original_attr();
|
|
|
|
argv = GetCommandLine();
|
|
len = (DWORD)wcslen( argv ) + 1;
|
|
if (len < MAX_PATH)
|
|
len = MAX_PATH;
|
|
arg = malloc( TSIZE(len) );
|
|
get_arg( arg, &argv, &cmd ); // skip the program name
|
|
get_arg( arg, &argv, &cmd );
|
|
|
|
if (*arg)
|
|
{
|
|
if (wcscmp( arg, L"/?" ) == 0 ||
|
|
wcscmp( arg, L"--help" ) == 0)
|
|
{
|
|
help();
|
|
return rc;
|
|
}
|
|
if (wcscmp( arg, L"--version" ) == 0)
|
|
{
|
|
_putws( L"ANSICON (" BITS L"-bit) version " PVERS L" (" PDATE L")." );
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
hHeap = HeapCreate( 0, 0, 65 * 1024 );
|
|
|
|
prog = get_program_name( NULL );
|
|
*buf = '\0';
|
|
GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) );
|
|
log_level = _wtoi( buf );
|
|
|
|
#ifdef _WIN64
|
|
if (*arg == '-' && arg[1] == 'P')
|
|
{
|
|
pi.dwProcessId = _wtoi( arg + 2 );
|
|
pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId );
|
|
if (pi.hProcess == NULL)
|
|
{
|
|
DEBUGSTR( 1, " Unable to open process %u (%u)",
|
|
pi.dwProcessId, GetLastError() );
|
|
}
|
|
else
|
|
{
|
|
Inject( &pi, &gui, NULL );
|
|
CloseHandle( pi.hProcess );
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (log_level)
|
|
DEBUGSTR( 1, NULL ); // start a new session
|
|
|
|
shell = run = TRUE;
|
|
|
|
while (*arg == '-')
|
|
{
|
|
switch (arg[1])
|
|
{
|
|
case 'l':
|
|
SetEnvironmentVariable( L"ANSICON_LOG", arg + 2 );
|
|
log_level = _wtoi( arg + 2 );
|
|
DEBUGSTR( 1, NULL ); // create a session
|
|
break;
|
|
|
|
case 'i':
|
|
case 'I':
|
|
case 'u':
|
|
case 'U':
|
|
shell = FALSE;
|
|
process_autorun( arg[1] );
|
|
if (arg[1] == 'u' || arg[1] == 'U')
|
|
break;
|
|
// else fall through
|
|
|
|
case 'p':
|
|
shell = FALSE;
|
|
if (GetParentProcessInfo( &pi, arg ))
|
|
{
|
|
pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
|
|
RemoteLoad( &pi, arg );
|
|
CloseHandle( pi.hProcess );
|
|
}
|
|
else
|
|
{
|
|
fputws( L"ANSICON: could not obtain the parent process.\n", stderr );
|
|
rc = 1;
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
{
|
|
int a = wcstol( arg + 2, NULL, 16 );
|
|
if (a == 0)
|
|
a = (arg[2] == '-') ? -7 : 7;
|
|
if (a < 0)
|
|
{
|
|
SetEnvironmentVariable( L"ANSICON_REVERSE", L"1" );
|
|
a = -a;
|
|
a = ((a >> 4) & 15) | ((a & 15) << 4);
|
|
}
|
|
SetConsoleTextAttribute( hConOut, (WORD)a );
|
|
SetEnvironmentVariable( L"ANSICON_DEF", NULL );
|
|
break;
|
|
}
|
|
|
|
case 'e':
|
|
case 'E':
|
|
case 't':
|
|
case 'T':
|
|
run = FALSE;
|
|
++arg;
|
|
goto arg_out;
|
|
}
|
|
get_arg( arg, &argv, &cmd );
|
|
}
|
|
arg_out:
|
|
if (run && *cmd == '\0')
|
|
{
|
|
if (!shell)
|
|
run = FALSE;
|
|
else if (!_isatty( 0 ))
|
|
{
|
|
*arg = 't';
|
|
run = FALSE;
|
|
}
|
|
}
|
|
|
|
// Ensure the default attributes are the current attributes.
|
|
if (GetEnvironmentVariable( L"ANSICON_DEF", buf, lenof(buf) ) != 0)
|
|
{
|
|
int a = wcstol( buf, NULL, 16 );
|
|
if (a < 0)
|
|
a = ((-a >> 4) & 15) | ((-a & 15) << 4);
|
|
SetConsoleTextAttribute( hConOut, (WORD)a );
|
|
}
|
|
|
|
if (run)
|
|
{
|
|
if (*cmd == '\0')
|
|
{
|
|
if (GetEnvironmentVariable( L"ComSpec", arg, MAX_PATH ))
|
|
cmd = arg;
|
|
else
|
|
{
|
|
// CreateProcessW writes to the string, so can't simply point to "cmd".
|
|
static TCHAR cmdstr[] = L"cmd";
|
|
cmd = cmdstr;
|
|
}
|
|
}
|
|
|
|
ZeroMemory( &si, sizeof(si) );
|
|
si.cb = sizeof(si);
|
|
if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED,
|
|
NULL, NULL, &si, &pi ))
|
|
{
|
|
Inject( &pi, &gui, arg );
|
|
ResumeThread( pi.hThread );
|
|
if (!gui)
|
|
{
|
|
SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE );
|
|
WaitForSingleObject( pi.hProcess, INFINITE );
|
|
GetExitCodeProcess( pi.hProcess, (LPDWORD)(LPVOID)&rc );
|
|
}
|
|
CloseHandle( pi.hProcess );
|
|
CloseHandle( pi.hThread );
|
|
}
|
|
else
|
|
{
|
|
print_error( arg );
|
|
rc = 1;
|
|
}
|
|
}
|
|
else if (*arg)
|
|
{
|
|
ansi = LoadLibrary( ANSIDLL );
|
|
if (ansi == NULL)
|
|
{
|
|
print_error( ANSIDLL );
|
|
rc = 1;
|
|
}
|
|
else if (*arg == 'e' || *arg == 'E')
|
|
{
|
|
cmd += 2;
|
|
if (*cmd == ' ' || *cmd == '\t')
|
|
++cmd;
|
|
fputws( cmd, stdout );
|
|
if (*arg == 'e')
|
|
_putws( L"" );
|
|
}
|
|
else // (*arg == 't' || *arg == 'T')
|
|
{
|
|
BOOL title = (*arg == 'T');
|
|
get_file( arg, &argv, &cmd );
|
|
if (*arg == '\0')
|
|
wcscpy( arg, L"-" );
|
|
do
|
|
{
|
|
if (title)
|
|
{
|
|
wprintf( L"==> %s <==\n", arg );
|
|
display( arg, title );
|
|
_putws( L"" );
|
|
}
|
|
else
|
|
display( arg, title );
|
|
get_file( arg, &argv, &cmd );
|
|
} while (*arg);
|
|
}
|
|
FreeLibrary( ansi );
|
|
}
|
|
|
|
set_original_attr();
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
// Display a file.
|
|
void display( LPCTSTR name, BOOL title )
|
|
{
|
|
HANDLE in, out;
|
|
BOOL pipe;
|
|
char buf[8192];
|
|
DWORD len;
|
|
|
|
if (*name == '-' && name[1] == '\0')
|
|
{
|
|
pipe = TRUE;
|
|
in = GetStdHandle( STD_INPUT_HANDLE );
|
|
}
|
|
else
|
|
{
|
|
pipe = FALSE;
|
|
in = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0, NULL );
|
|
if (in == INVALID_HANDLE_VALUE)
|
|
{
|
|
print_error( name );
|
|
return;
|
|
}
|
|
}
|
|
if (title)
|
|
{
|
|
_putws( L"" );
|
|
// Need to flush, otherwise it's written *after* STD_OUTPUT_HANDLE should
|
|
// it be redirected.
|
|
fflush( stdout );
|
|
}
|
|
out = GetStdHandle( STD_OUTPUT_HANDLE );
|
|
for (;;)
|
|
{
|
|
if (!ReadFile( in, buf, sizeof(buf), &len, NULL ))
|
|
{
|
|
if (GetLastError() != ERROR_BROKEN_PIPE)
|
|
print_error( name );
|
|
break;
|
|
}
|
|
if (len == 0)
|
|
break;
|
|
WriteFile( out, buf, len, &len, NULL );
|
|
}
|
|
if (!pipe)
|
|
CloseHandle( in );
|
|
}
|
|
|
|
|
|
void print_error( LPCTSTR name )
|
|
{
|
|
LPTSTR errmsg = NULL;
|
|
DWORD err = GetLastError();
|
|
|
|
fputws( L"ANSICON: ", stderr );
|
|
if (err == ERROR_BAD_EXE_FORMAT)
|
|
{
|
|
// This error requires an argument, which is name.
|
|
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, (va_list*)&name );
|
|
fputws( errmsg, stderr );
|
|
}
|
|
else
|
|
{
|
|
if (FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL ))
|
|
fwprintf( stderr, L"%s: %s", name, errmsg );
|
|
else
|
|
fwprintf( stderr, L"%s: Error %lu.\n", name, err );
|
|
}
|
|
LocalFree( errmsg );
|
|
}
|
|
|
|
|
|
// Add or remove ANSICON to AutoRun.
|
|
void process_autorun( TCHAR cmd )
|
|
{
|
|
HKEY cmdkey;
|
|
TCHAR ansicon[MAX_PATH+80];
|
|
TCHAR logstr[80];
|
|
LPTSTR autorun, ansirun;
|
|
DWORD len, type, exist;
|
|
BOOL inst;
|
|
|
|
if (log_level)
|
|
_snwprintf( logstr, lenof(logstr), L"set ANSICON_LOG=%d&", log_level );
|
|
else
|
|
*logstr = '\0';
|
|
len = TSIZE(_snwprintf( ansicon, lenof(ansicon),
|
|
L"(if %%ANSICON_VER%%==^%%ANSICON_VER^%% %s\"%s\" -p)",
|
|
logstr, prog_path ) + 1);
|
|
|
|
inst = (towlower( cmd ) == 'i');
|
|
if (RegCreateKeyEx( (iswlower(cmd)) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
|
|
CMDKEY, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
|
|
&cmdkey, &exist ) != ERROR_SUCCESS)
|
|
{
|
|
fputws( L"ANSICON: could not update AutoRun", stderr );
|
|
if (iswupper( cmd ))
|
|
fwprintf( stderr, L" (perhaps use -%c, or run as admin)", towlower(cmd) );
|
|
fputws( L".\n", stderr );
|
|
}
|
|
exist = 0;
|
|
RegQueryValueEx( cmdkey, AUTORUN, NULL, NULL, NULL, &exist );
|
|
if (exist == 0)
|
|
{
|
|
if (inst)
|
|
RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len );
|
|
}
|
|
else
|
|
{
|
|
// Let's assume there's sufficient memory.
|
|
autorun = malloc( exist + len );
|
|
RegQueryValueEx( cmdkey, AUTORUN, NULL, &type, (PBYTE)autorun, &exist );
|
|
// Remove the existing command, if present.
|
|
ansirun = wcsstr( autorun, L"(if %ANSICON_VER%" );
|
|
if (ansirun != NULL)
|
|
{
|
|
LPTSTR tmp = wcschr( ansirun, '"' ); // opening quote
|
|
tmp = wcschr( tmp + 1, '"' ); // closing quote
|
|
tmp = wcschr( tmp + 1, ')' ); // closing bracket
|
|
if (*++tmp == '&')
|
|
++tmp;
|
|
if (*tmp == '&')
|
|
++tmp;
|
|
if (*tmp == '\0')
|
|
{
|
|
if (ansirun > autorun && ansirun[-1] == '&')
|
|
--ansirun;
|
|
if (ansirun > autorun && ansirun[-1] == '&')
|
|
--ansirun;
|
|
}
|
|
wcscpy( ansirun, tmp );
|
|
exist = TSIZE((DWORD)wcslen( autorun ) + 1);
|
|
}
|
|
if (inst)
|
|
{
|
|
if (exist == sizeof(TCHAR))
|
|
RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len );
|
|
else
|
|
{
|
|
memmove( (PBYTE)autorun + len, autorun, exist );
|
|
memcpy( autorun, ansicon, len );
|
|
((PBYTE)autorun)[len-sizeof(TCHAR)] = '&';
|
|
RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist+len );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (exist == sizeof(TCHAR))
|
|
RegDeleteValue( cmdkey, AUTORUN );
|
|
else
|
|
RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist );
|
|
}
|
|
free( autorun );
|
|
}
|
|
RegCloseKey( cmdkey );
|
|
}
|
|
|
|
|
|
// Search each process in the snapshot for id.
|
|
BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe )
|
|
{
|
|
BOOL fOk;
|
|
|
|
ppe->dwSize = sizeof(PROCESSENTRY32);
|
|
for (fOk = Process32First( snap, ppe ); fOk; fOk = Process32Next( snap, ppe ))
|
|
if (ppe->th32ProcessID == id)
|
|
break;
|
|
|
|
return fOk;
|
|
}
|
|
|
|
|
|
// Obtain the process identifier of the parent process.
|
|
BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
|
|
{
|
|
HANDLE hSnap;
|
|
PROCESSENTRY32 pe;
|
|
BOOL fOk;
|
|
|
|
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
|
|
if (hSnap == INVALID_HANDLE_VALUE)
|
|
{
|
|
DEBUGSTR( 1, "Failed to create snapshot (%u)", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
fOk = (find_proc_id( hSnap, GetCurrentProcessId(), &pe ) &&
|
|
find_proc_id( hSnap, pe.th32ParentProcessID, &pe ));
|
|
CloseHandle( hSnap );
|
|
if (!fOk)
|
|
{
|
|
DEBUGSTR( 1, "Failed to locate parent" );
|
|
return FALSE;
|
|
}
|
|
|
|
ppi->dwProcessId = pe.th32ProcessID;
|
|
wcscpy( name, pe.szExeFile );
|
|
|
|
return fOk;
|
|
}
|
|
|
|
|
|
// Return the first non-space character from arg.
|
|
LPTSTR skip_spaces( LPTSTR arg )
|
|
{
|
|
while (*arg == ' ' || *arg == '\t')
|
|
++arg;
|
|
|
|
return arg;
|
|
}
|
|
|
|
|
|
// Retrieve an argument from the command line. cmd gets the existing argv; argv
|
|
// is ready for the next argument.
|
|
void get_arg( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
|
|
{
|
|
LPTSTR line;
|
|
|
|
line = *cmd = skip_spaces( *argv );
|
|
while (*line != '\0')
|
|
{
|
|
if (*line == ' ' || *line == '\t')
|
|
{
|
|
++line;
|
|
break;
|
|
}
|
|
if (*line == '"')
|
|
{
|
|
while (*++line != '\0')
|
|
{
|
|
if (*line == '"')
|
|
{
|
|
++line;
|
|
break;
|
|
}
|
|
*arg++ = *line;
|
|
}
|
|
}
|
|
else
|
|
*arg++ = *line++;
|
|
}
|
|
*arg = '\0';
|
|
*argv = line;
|
|
}
|
|
|
|
|
|
int glob_sort( const void* a, const void* b )
|
|
{
|
|
return lstrcmpi( *(LPCTSTR*)a, *(LPCTSTR*)b );
|
|
}
|
|
|
|
|
|
// As get_arg, but expand wildcards.
|
|
void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
|
|
{
|
|
HANDLE fh, in;
|
|
WIN32_FIND_DATA fd;
|
|
LPTSTR path;
|
|
int size;
|
|
char buf[1024];
|
|
static LPTSTR name;
|
|
static LPTSTR* glob;
|
|
static int globbed;
|
|
|
|
if (globbed != 0)
|
|
{
|
|
if (glob[globbed] == NULL)
|
|
{
|
|
free( glob );
|
|
globbed = 0;
|
|
}
|
|
else
|
|
{
|
|
wcscpy( name, glob[globbed++] );
|
|
return;
|
|
}
|
|
}
|
|
|
|
get_arg( arg, argv, cmd );
|
|
if (wcspbrk( arg, L"*?" ) != NULL)
|
|
{
|
|
fh = FindFirstFile( arg, &fd );
|
|
if (fh != INVALID_HANDLE_VALUE)
|
|
{
|
|
size = 0;
|
|
do
|
|
{
|
|
if (! (fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
|
|
FILE_ATTRIBUTE_HIDDEN)))
|
|
{
|
|
++globbed;
|
|
size += (int)wcslen( fd.cFileName ) + 1;
|
|
}
|
|
} while (FindNextFile( fh, &fd ));
|
|
FindClose( fh );
|
|
|
|
if (globbed != 0)
|
|
{
|
|
for (path = name = arg; *path != '\0'; ++path)
|
|
if (*path == '\\' || *path == '/')
|
|
name = path + 1;
|
|
glob = malloc( (globbed + 1) * PTRSZ + TSIZE(size) );
|
|
path = (LPTSTR)(glob + globbed + 1);
|
|
globbed = 0;
|
|
fh = FindFirstFile( arg, &fd );
|
|
do
|
|
{
|
|
if (! (fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
|
|
FILE_ATTRIBUTE_HIDDEN)))
|
|
{
|
|
// Ignore apparent binary files.
|
|
wcscpy( name, fd.cFileName );
|
|
in = CreateFile( arg, GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0, NULL );
|
|
if (in != INVALID_HANDLE_VALUE)
|
|
{
|
|
ReadFile( in, buf, sizeof(buf), (LPVOID)&size, NULL );
|
|
CloseHandle( in );
|
|
if (memchr( buf, 0, size ) != NULL)
|
|
continue;
|
|
}
|
|
size = (int)wcslen( fd.cFileName ) + 1;
|
|
memcpy( path, fd.cFileName, TSIZE(size) );
|
|
glob[globbed++] = path;
|
|
path += size;
|
|
}
|
|
} while (FindNextFile( fh, &fd ));
|
|
FindClose( fh );
|
|
glob[globbed] = NULL;
|
|
|
|
qsort( glob, globbed, PTRSZ, glob_sort );
|
|
|
|
wcscpy( name, glob[0] );
|
|
globbed = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// VC macros don't like preprocessor statements mixed with strings.
|
|
#ifdef _WIN64
|
|
#define WINTYPE L"Windows"
|
|
#else
|
|
#define WINTYPE L"Win32"
|
|
#endif
|
|
|
|
void help( void )
|
|
{
|
|
_putws(
|
|
L"ANSICON by Jason Hood <jadoxa@yahoo.com.au>.\n"
|
|
L"Version " PVERS L" (" PDATE L"). Freeware.\n"
|
|
L"http://ansicon.adoxa.vze.com/\n"
|
|
L"\n"
|
|
L"Process ANSI escape sequences in " WINTYPE L" console programs.\n"
|
|
L"\n"
|
|
L"ansicon [-l<level>] [-i] [-I] [-u] [-U] [-m[<attr>]] [-p]\n"
|
|
L" [-e|E string | -t|T [file(s)] | program [args]]\n"
|
|
L"\n"
|
|
L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n"
|
|
L" \t\t +4=output, +8=append) for program (-p is unaffected)\n"
|
|
L" -i\t\tinstall - add ANSICON to CMD's AutoRun entry (also implies -p)\n"
|
|
L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n"
|
|
L" -I -U\t\tuse local machine instead of current user\n"
|
|
L" -m\t\tuse grey on black (\"monochrome\") or <attr> as default color\n"
|
|
L" -p\t\thook into the parent process\n"
|
|
L" -e\t\techo string\n"
|
|
L" -E\t\techo string, don't append newline\n"
|
|
L" -t\t\tdisplay files (\"-\" for stdin), combined as a single stream\n"
|
|
L" -T\t\tdisplay files, name first, blank line before and after\n"
|
|
L" program\trun the specified program\n"
|
|
L" nothing\trun a new command processor, or display stdin if redirected\n"
|
|
L"\n"
|
|
L"<attr> is one or two hexadecimal digits; please use \"COLOR /?\" for details.\n"
|
|
L"It may start with '-' to reverse foreground and background (but not for -p)."
|
|
);
|
|
}
|