Exclude modules from being hooked; hook only selected GUI programs.

Added environment variable ANSICON_EXC to specify modules that should not be
hooked.  This should work around the nvd3d9wrap.dll issue.  Since it helps to
know what the modules are, logging is now always available, controlled by -l
or ANSICON_LOG.  A side-effect caused debugstr.c to move to util.c.

GUI programs are once again not hooked, unless run by "ansicon" directly or in
the ANSICON_GUI environment variable.  Since not hooking still leaves ANSICON in
the environment, created ANSICON_VER as a dynamic-only variable, which can also
serve as a version check.

Due to an email requesting a reverse video option, realised I always take the
current attributes as default.	This means if you turned on reverse and ran a
program, it would take the reverse as its default.  Created ANSICON_DEF variable
to explicitly set the default attribute, using the current if it doesn't exist.
The reverse video option is done via a "negative" attribute (e.g. "-m-f0" is
reversed black on white, meaning you'll get white on black, with foreground
sequences changing the background).  (The difference from "\e[7m" is that it
won't be reset on "\e[m".)

A child program will inherit the parent's modes (but not shift); the parent will
read the child's modes on exit (but not unload).  The exception is "ansicon",
which will always start with the default modes and leave the parent unchanged.

Improved the AutoRun entry, only running "ansicon" if ANSICON_VER doesn't exist.
The "ansicon" command is always first.

Stopped -u implying -p; return the program's exit code; don't restore the
original color when just using -p; output error messages to stderr.
This commit is contained in:
Jason Hood 2011-12-14 20:53:51 +10:00
parent a87891fa6a
commit 790de57763
15 changed files with 1295 additions and 845 deletions

913
ANSI.c

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,57 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
========================================
getopt, getopt_long, and getop_long_only
========================================
Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Sponsored in part by the Defense Advanced Research Projects
Agency (DARPA) and Air Force Research Laboratory, Air Force
Materiel Command, USAF, under agreement number F39502-99-1-0512.
* * * * * * *
Copyright (c) 2000 The NetBSD Foundation, Inc.
All rights reserved.
This code is derived from software contributed to The NetBSD Foundation
by Dieter Baron and Thomas Klausner.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
===============================================================
gdtoa: Converting between IEEE floating point numbers and ASCII
@ -167,3 +218,23 @@ permission from the author and then add a license into the Cephes files
in MinGW runtime. At least on follow-up it is marked that debian sees the
version a-like BSD one. As MinGW.org (where those cephes parts are coming
from) distributes them now over 6 years, it should be fine.
===================================
Headers and IDLs imported from Wine
===================================
Some header and IDL files were imported from the Wine project. These files
are prominent maked in source. Their copyright belongs to contributors and
they are distributed under LGPL license.
Disclaimer
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

View File

@ -5,6 +5,7 @@
*/
#include <winver.h>
#include "version.h"
#ifdef _WIN64
# define BITS "64"
@ -13,8 +14,8 @@
#endif
1 VERSIONINFO
FILEVERSION 1,4,0,0
PRODUCTVERSION 1,4,0,0
FILEVERSION PVERB
PRODUCTVERSION PVERB
FILEOS VOS_NT
FILETYPE VFT_DLL
{
@ -25,12 +26,12 @@ FILETYPE VFT_DLL
VALUE "Comments", "http://ansicon.adoxa.cjb.net/"
VALUE "CompanyName", "Jason Hood"
VALUE "FileDescription", "ANSI Console"
VALUE "FileVersion", "1.40"
VALUE "FileVersion", PVERSA
VALUE "InternalName", "ANSI" BITS
VALUE "LegalCopyright", "Freeware"
VALUE "OriginalFilename", "ANSI" BITS ".dll"
VALUE "ProductName", "ANSICON"
VALUE "ProductVersion", "1.40"
VALUE "ProductVersion", PVERSA
}
}

664
ansicon.c
View File

@ -47,18 +47,24 @@
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.
*/
#define PVERS L"1.40"
#define PDATE L"1 March, 2011"
#define PDATE L"14 December, 2011"
#include "ansicon.h"
#include <shellapi.h>
#include "version.h"
#include <tlhelp32.h>
#include <ctype.h>
#include <io.h>
#include <objbase.h>
#include <psapi.h>
#ifdef __MINGW32__
int _CRT_glob = 0;
@ -79,33 +85,33 @@ int _CRT_glob = 0;
void help( void );
void display( LPCTSTR, BOOL );
void print_error( LPCTSTR, ... );
LPTSTR skip_spaces( LPTSTR );
LPTSTR skip_arg( LPTSTR );
void get_arg( LPTSTR, LPTSTR*, LPTSTR* );
void process_autorun( TCHAR );
BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe );
BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi );
BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR );
// Find the name of the DLL and inject it.
BOOL Inject( LPPROCESS_INFORMATION ppi )
BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
{
DWORD len;
WCHAR dll[MAX_PATH];
int type;
#if (MYDEBUG > 0)
if (GetModuleFileNameEx( ppi->hProcess, NULL, dll, lenof(dll) ))
DEBUGSTR( L"%s", dll );
#endif
type = ProcessType( ppi );
DEBUGSTR( 1, L"%s", app );
type = ProcessType( ppi, gui );
if (type == 0)
{
fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app );
return FALSE;
}
len = GetModuleFileName( NULL, dll, lenof(dll) );
while (dll[len-1] != '\\')
--len;
len = (DWORD)(prog - prog_path);
memcpy( dll, prog_path, TSIZE(len) );
#ifdef _WIN64
wsprintf( dll + len, L"ANSI%d.dll", type );
if (type == 32)
@ -145,288 +151,302 @@ DWORD CtrlHandler( DWORD event )
}
//int _tmain( int argc, TCHAR* argv[] )
int main( void )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
TCHAR* cmd;
BOOL option;
BOOL opt_m;
LPTSTR argv, arg, cmd;
TCHAR logstr[4];
BOOL installed;
BOOL shell, run, gui;
HMODULE ansi;
DWORD len;
int rc = 0;
int argc;
LPWSTR* argv = CommandLineToArgvW( GetCommandLine(), &argc );
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 (argc > 1)
if (*arg)
{
if (lstrcmp( argv[1], L"--help" ) == 0 ||
(argv[1][0] == '-' && (argv[1][1] == '?' || argv[1][1] == 'h')) ||
(argv[1][0] == '/' && argv[1][1] == '?'))
if (wcscmp( arg, L"/?" ) == 0 ||
wcscmp( arg, L"--help" ) == 0)
{
help();
return rc;
}
if (lstrcmp( argv[1], L"--version" ) == 0)
if (wcscmp( arg, L"--version" ) == 0)
{
_putws( L"ANSICON (" BITS L"-bit) version " PVERS L" (" PDATE L")." );
return rc;
}
}
#if (MYDEBUG > 1)
DEBUGSTR( NULL ); // create a new file
#endif
prog = get_program_name( NULL );
*logstr = '\0';
GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) );
log_level = _wtoi( logstr );
if (log_level && !(log_level & 8))
DEBUGSTR( 1, NULL ); // create a new file
option = (argc > 1 && argv[1][0] == '-');
if (option && (towlower( argv[1][1] ) == 'i' ||
towlower( argv[1][1] ) == 'u'))
installed = (GetEnvironmentVariable( L"ANSICON_VER", NULL, 0 ) != 0);
// If it's already installed, remove it. This serves two purposes: preserves
// the parent's GRM; and unconditionally injects into GUI, without having to
// worry about ANSICON_GUI.
if (installed)
{
process_autorun( argv[1][1] );
argv[1][1] = 'p';
fputws( L"\33[m", stdout );
FreeLibrary( GetModuleHandle( L"ANSI" BITS L".dll" ) );
}
shell = run = TRUE;
get_original_attr();
opt_m = FALSE;
if (option && argv[1][1] == 'm')
while (*arg == '-')
{
WORD attr = 7;
if (iswxdigit( argv[1][2] ))
switch (arg[1])
{
attr = iswdigit( argv[1][2] ) ? argv[1][2] - '0'
: (argv[1][2] | 0x20) - 'a' + 10;
if (iswxdigit( argv[1][3]))
{
attr <<= 4;
attr |= iswdigit( argv[1][3] ) ? argv[1][3] - '0'
: (argv[1][3] | 0x20) - 'a' + 10;
}
}
SetConsoleTextAttribute( hConOut, attr );
case 'l':
SetEnvironmentVariable( L"ANSICON_LOG", arg + 2 );
log_level = _wtoi( arg + 2 );
if (!(log_level & 8)) // unless told otherwise
DEBUGSTR( 1, NULL ); // create a new file
break;
opt_m = TRUE;
++argv;
--argc;
option = (argc > 1 && argv[1][0] == '-');
}
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
installed = (GetEnvironmentVariable( L"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 );
if (!Inject( &pi ))
{
_putws( L"ANSICON: parent process type is not supported." );
rc = 1;
}
ResumeThread( pi.hThread );
CloseHandle( pi.hThread );
CloseHandle( pi.hProcess );
}
else
{
_putws( L"ANSICON: could not obtain the parent process." );
rc = 1;
}
}
else
{
ansi = 0;
if (!installed)
{
ansi = LoadLibrary( L"ANSI" BITS L".dll" );
if (!ansi)
{
fputws( L"ANSICON: failed to load ANSI" BITS L".dll.\n", stderr );
rc = 1;
}
}
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)
wprintf( L"==> %s <==\n", argv[2] );
display( argv[2], title );
if (title)
putwchar( '\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'))
{
fputws( cmd + 3, stdout );
if (cmd[1] == 'e')
putwchar( '\n' );
}
else if (!_isatty( 0 ) && *cmd == '\0')
{
display( L"-", FALSE );
}
else
{
if (*cmd == '\0')
case 'p':
shell = FALSE;
// If it's already installed, there's no need to do anything.
if (installed)
{
cmd = _wgetenv( L"ComSpec" );
if (cmd == NULL)
cmd = L"cmd";
DEBUGSTR( 1, L"Already installed" );
}
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
if (CreateProcess( NULL, cmd, NULL,NULL, TRUE, 0, NULL,NULL, &si, &pi ))
else if (GetParentProcessInfo( &pi, arg ))
{
BOOL console = FALSE;
TCHAR name[MAX_PATH];
DWORD rc;
CoInitialize( NULL );
do
{
// When I first tried doing this, it took a little while to
// succeed. Testing again shows it works immediately - perhaps the
// CoInitialize introduces enough of a delay. Still, play it safe
// and keep trying. And if you're wondering why I do it at all,
// ProcessType may detect GUI, even for a console process. That's
// fine after injection (including -p), but not here. We *need* to
// suspend our own execution whilst running the child, otherwise
// bad things happen (besides which, I want to restore the original
// attributes when the child exits).
if (GetModuleFileNameEx( pi.hProcess, NULL, name, lenof(name) ))
{
DWORD_PTR info;
info = SHGetFileInfo( name, 0, NULL, 0, SHGFI_EXETYPE );
if (info == 0x00004550) // console PE
console = TRUE;
DEBUGSTR( L"%s", name );
DEBUGSTR( L" %s (%p)", (console) ? L"Console" : L"Not console",
info );
break;
}
Sleep( 10 );
} while (GetExitCodeProcess( pi.hProcess, &rc ) &&
rc == STILL_ACTIVE);
CoUninitialize();
if (console)
{
SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE );
WaitForSingleObject( pi.hProcess, INFINITE );
}
CloseHandle( pi.hProcess );
pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId );
SuspendThread( pi.hThread );
if (!Inject( &pi, &gui, arg ))
rc = 1;
ResumeThread( pi.hThread );
CloseHandle( pi.hThread );
CloseHandle( pi.hProcess );
}
else
{
*skip_arg( cmd ) = '\0';
wprintf( L"ANSICON: '%s' could not be executed.\n", cmd );
fputws( L"ANSICON: could not obtain the parent process.\n", stderr );
rc = 1;
}
}
}
break;
if (ansi)
FreeLibrary( ansi );
case 'm':
{
int a = wcstol( arg + 2, NULL, 16 );
if (a == 0)
a = (arg[2] == '-') ? -7 : 7;
if (a < 0)
{
SetEnvironmentVariable( L"ANSICON_REVERSE", L"1" );
a = -a;
a = ((a >> 4) & 15) | ((a & 15) << 4);
}
SetConsoleTextAttribute( hConOut, a );
SetEnvironmentVariable( L"ANSICON_DEF", NULL );
break;
}
case 'e':
case 'E':
case 't':
case 'T':
run = FALSE;
++arg;
goto arg_out;
}
get_arg( arg, &argv, &cmd );
}
arg_out:
if (run && *cmd == '\0')
{
if (!_isatty( 0 ))
{
*arg = 't';
run = FALSE;
}
else if (!shell)
run = FALSE;
}
set_original_attr();
if (run)
{
if (*cmd == '\0')
{
cmd = _wgetenv( L"ComSpec" );
if (cmd == NULL)
cmd = L"cmd";
arg = cmd;
}
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED,
NULL, NULL, &si, &pi ))
{
Inject( &pi, &gui, arg );
ResumeThread( pi.hThread );
if (!gui)
{
SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE );
WaitForSingleObject( pi.hProcess, INFINITE );
GetExitCodeProcess( pi.hProcess, (LPDWORD)(LPVOID)&rc );
}
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
else
{
print_error( arg, arg );
rc = 1;
}
}
else if (*arg)
{
ansi = LoadLibrary( L"ANSI" BITS L".dll" );
if (ansi == NULL)
{
print_error( L"ANSI" BITS L".dll" );
rc = 1;
}
if (*arg == 'e' || *arg == 'E')
{
cmd += 2;
if (*cmd == ' ' || *cmd == '\t')
++cmd;
fputws( cmd, stdout );
if (*arg == 'e')
putwchar( '\n' );
}
else // (*arg == 't' || *arg == 'T')
{
BOOL title = (*arg == 'T');
get_arg( arg, &argv, &cmd );
if (*arg == '\0')
wcscpy( arg, L"-" );
do
{
if (title)
{
wprintf( L"==> %s <==\n", arg );
display( arg, title );
putwchar( '\n' );
}
else
display( arg, title );
get_arg( arg, &argv, &cmd );
} while (*arg);
}
FreeLibrary( ansi );
}
if (run || *arg)
set_original_attr();
else
CloseHandle( hConOut );
return rc;
}
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)
wprintf( L"ANSICON: %s: ", name );
fputws( errmsg, stdout );
LocalFree( errmsg );
}
// Display a file.
void display( LPCTSTR name, BOOL title )
{
HANDLE file;
int c;
LARGE_INTEGER size, offset;
HANDLE in, out;
BOOL pipe;
char buf[8192];
DWORD len;
// Handle the pipe differently.
if (*name == '-' && name[1] == '\0')
{
if (title)
putwchar( '\n' );
while ((c = getchar()) != EOF)
putchar( c );
return;
pipe = TRUE;
in = GetStdHandle( STD_INPUT_HANDLE );
}
file = CreateFile( name, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL );
if (file == INVALID_HANDLE_VALUE)
else
{
print_error( name, title );
return;
}
GetFileSizeEx( file, &size );
if (size.QuadPart != 0)
{
HANDLE map = CreateFileMapping( file, NULL, PAGE_READONLY, 0, 0, NULL );
if (map)
pipe = FALSE;
in = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if (in == INVALID_HANDLE_VALUE)
{
if (title)
putwchar( '\n' );
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 );
print_error( name );
return;
}
else
print_error( name, title );
}
CloseHandle( file );
if (title)
{
putwchar( '\n' );
// Need to flush, otherwise it's written *after* STD_OUTPUT_HANDLE should
// it be redirected.
fflush( stdout );
}
out = GetStdHandle( STD_OUTPUT_HANDLE );
for (;;)
{
if (!ReadFile( in, buf, sizeof(buf), &len, NULL ))
{
if (GetLastError() != ERROR_BROKEN_PIPE)
print_error( name );
break;
}
if (len == 0)
break;
WriteFile( out, buf, len, &len, NULL );
}
if (!pipe)
CloseHandle( in );
}
void print_error( LPCTSTR name, ... )
{
LPTSTR errmsg = NULL;
DWORD err = GetLastError();
va_list arg;
if (err == ERROR_BAD_EXE_FORMAT)
{
// This error requires an argument, which is a duplicate of name.
va_start( arg, name );
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, &arg );
va_end( arg );
fwprintf( stderr, L"ANSICON: %s", errmsg );
}
else
{
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL );
// Just in case there are other messages requiring args...
if (errmsg == NULL)
fwprintf( stderr, L"ANSICON: %s: Error %lu.\n", name, err );
else
fwprintf( stderr, L"ANSICON: %s: %s", name, errmsg );
}
LocalFree( errmsg );
}
@ -434,16 +454,19 @@ void display( LPCTSTR name, BOOL title )
void process_autorun( TCHAR cmd )
{
HKEY cmdkey;
TCHAR ansicon[MAX_PATH+8];
TCHAR ansicon[MAX_PATH+80];
TCHAR logstr[80];
LPTSTR autorun, ansirun;
DWORD len, type, exist;
BOOL inst;
len = GetModuleFileName( NULL, ansicon+2, MAX_PATH );
ansicon[0] = '&';
ansicon[1] = ansicon[2+len] = '"';
wcscpy( ansicon + 3+len, L" -p" );
len += 6;
if (log_level)
_snwprintf( logstr, lenof(logstr), L"set ANSICON_LOG=%d&", log_level );
else
*logstr = '\0';
len = TSIZE(_snwprintf( ansicon, lenof(ansicon),
L"(if %%ANSICON_VER%%==^%%ANSICON_VER^%% %s\"%s\" -p)",
logstr, prog_path ) + 1);
inst = (towlower( cmd ) == 'i');
RegCreateKeyEx( (iswlower( cmd )) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
@ -452,48 +475,58 @@ void process_autorun( TCHAR cmd )
&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))
if (exist == 0)
{
exist += sizeof(TCHAR);
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 );
ansirun = wcsstr( autorun, ansicon+1 );
// 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 (!ansirun)
if (exist == sizeof(TCHAR))
RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len );
else
{
wcscpy( (LPTSTR)((PBYTE)autorun + exist - sizeof(TCHAR)), ansicon );
RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun,
exist + len*sizeof(TCHAR) );
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 (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) );
}
}
if (exist == sizeof(TCHAR))
RegDeleteValue( cmdkey, AUTORUN );
else
RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist );
}
free( autorun );
}
else if (inst)
{
RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)(ansicon+1),
len*sizeof(TCHAR) );
}
free( autorun );
RegCloseKey( cmdkey );
}
@ -513,20 +546,19 @@ BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe )
// Obtain the process and thread identifiers of the parent process.
BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi )
BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
{
HANDLE hSnap;
PROCESSENTRY32 pe;
THREADENTRY32 te;
DWORD id = GetCurrentProcessId();
BOOL fOk;
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, id );
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, 0 );
if (hSnap == INVALID_HANDLE_VALUE)
return FALSE;
find_proc_id( hSnap, id, &pe );
find_proc_id( hSnap, GetCurrentProcessId(), &pe );
if (!find_proc_id( hSnap, pe.th32ParentProcessID, &pe ))
{
CloseHandle( hSnap );
@ -542,38 +574,53 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi )
ppi->dwProcessId = pe.th32ProcessID;
ppi->dwThreadId = te.th32ThreadID;
wcscpy( name, pe.szExeFile );
return fOk;
}
// Return the first non-space character from cmd.
LPTSTR skip_spaces( LPTSTR cmd )
// Return the first non-space character from arg.
LPTSTR skip_spaces( LPTSTR arg )
{
while ((*cmd == ' ' || *cmd == '\t') && *cmd != '\0')
++cmd;
while (*arg == ' ' || *arg == '\t')
++arg;
return cmd;
return arg;
}
// Return the end of the argument at cmd.
LPTSTR skip_arg( LPTSTR cmd )
// 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 )
{
while (*cmd != ' ' && *cmd != '\t' && *cmd != '\0')
{
if (*cmd == '"')
{
do
++cmd;
while (*cmd != '"' && *cmd != '\0');
if (*cmd == '\0')
--cmd;
}
++cmd;
}
LPTSTR line;
return cmd;
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;
}
@ -590,10 +637,12 @@ L"Process ANSI escape sequences in Windows console programs.\n"
L"Process ANSI escape sequences in Win32 console programs.\n"
#endif
L"\n"
L"ansicon -i|I | -u|U\n"
L"ansicon [-m[<attr>]] [-p | -e|E string | -t|T [file(s)] | program [args]]\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" -i\t\tinstall - add ANSICON to the AutoRun entry (implies -p)\n"
L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n"
L" \t\t +4=output, +8=append) for program (-p is unaffected)\n"
L" -i\t\tinstall - add ANSICON to the AutoRun entry (also implies -p)\n"
L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n"
L" -I -U\t\tuse local machine instead of current user\n"
L" -m\t\tuse grey on black (\"monochrome\") or <attr> as default color\n"
@ -605,6 +654,7 @@ 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."
);
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)."
);
}

View File

@ -18,29 +18,30 @@
#include <stdlib.h>
#define lenof(array) (sizeof(array)/sizeof(*(array)))
#define TSIZE(size) ((size) * sizeof(TCHAR))
int ProcessType( LPPROCESS_INFORMATION );
typedef struct
{
BYTE foreground; // ANSI base color (0 to 7; add 30)
BYTE background; // ANSI base color (0 to 7; add 40)
BYTE bold; // console FOREGROUND_INTENSITY bit
BYTE underline; // console BACKGROUND_INTENSITY bit
BYTE rvideo; // swap foreground/bold & background/underline
BYTE concealed; // set foreground/bold to background/underline
BYTE reverse; // swap console foreground & background attributes
} GRM, *PGRM; // Graphic Rendition Mode
int ProcessType( LPPROCESS_INFORMATION, BOOL* );
void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR );
void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR );
extern TCHAR prog_path[MAX_PATH];
extern LPTSTR prog;
LPTSTR get_program_name( LPTSTR );
// ========== Auxiliary debug function
#ifndef MYDEBUG
# define MYDEBUG 0 // 0 - no debugging
// 1 - use OutputDebugString
// 2 - use %temp%\ansicon.log
#endif
#if (MYDEBUG > 0)
void DEBUGSTR( LPTSTR szFormat, ... );
#else
# if defined(_MSC_VER) && _MSC_VER <= 1400
#define DEBUGSTR (void)
# else
# define DEBUGSTR(...)
# endif
#endif
extern int log_level;
void DEBUGSTR( int level, LPTSTR szFormat, ... );
#endif

View File

@ -5,10 +5,11 @@
*/
#include <winver.h>
#include "version.h"
1 VERSIONINFO
FILEVERSION 1,4,0,0
PRODUCTVERSION 1,4,0,0
FILEVERSION PVERB
PRODUCTVERSION PVERB
FILEOS VOS_NT
FILETYPE VFT_APP
{
@ -19,12 +20,12 @@ FILETYPE VFT_APP
VALUE "Comments", "http://ansicon.adoxa.cjb.net/"
VALUE "CompanyName", "Jason Hood"
VALUE "FileDescription", "ANSI Console"
VALUE "FileVersion", "1.40"
VALUE "FileVersion", PVERSA
VALUE "InternalName", "ansicon"
VALUE "LegalCopyright", "Freeware"
VALUE "OriginalFilename", "ansicon.exe"
VALUE "ProductName", "ANSICON"
VALUE "ProductVersion", "1.40"
VALUE "ProductVersion", PVERSA
}
}

View File

@ -1,102 +0,0 @@
/*
debugstr.c - Auxiliary debug functionality.
*/
#include "ansicon.h"
#if (MYDEBUG > 1)
char tempfile[MAX_PATH];
#endif
#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" ) );
if (szFormat == NULL)
{
DeleteFileA( tempfile );
return;
}
#endif
va_start( pArgList, szFormat );
_vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList );
va_end( pArgList );
szFormat = szBuffer;
if (*szFormat == '\33')
{
BOOL first = TRUE;
LPTSTR pos = szEscape;
while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4)
{
if (*szFormat < 32)
{
*pos++ = '\\';
switch (*szFormat)
{
case '\a': *pos++ = 'a'; break;
case '\b': *pos++ = 'b'; break;
case '\t': *pos++ = 't'; break;
case '\r': *pos++ = 'r'; break;
case '\n': *pos++ = 'n'; break;
case 27 : *pos++ = 'e'; break;
default:
pos += _snwprintf( pos, 32, L"%.*o",
(szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1,
*szFormat );
}
}
else if (*szFormat == '"')
{
if (first)
first = FALSE;
else if (szFormat[1] == '\0')
;
else
*pos++ = '\\';
*pos++ = '"';
}
else
{
*pos++ = *szFormat;
}
}
*pos = '\0';
szFormat = szEscape;
}
#if (MYDEBUG > 1)
{
HANDLE mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
DWORD wait = WaitForSingleObject( mutex, 500 );
FILE* file = fopen( tempfile, "at" ); // _fmode might be binary
if (file != NULL)
{
TCHAR path[MAX_PATH];
LPTSTR prog, ext;
GetModuleFileName( NULL, path, lenof(path) );
prog = wcsrchr( path, '\\' );
if (prog != NULL)
++prog;
else
prog = path;
ext = wcsrchr( prog, '.' );
if (ext != NULL)
*ext = '\0';
fwprintf( file, L"%s: %s\n", prog, szFormat );
fclose( file );
}
if (wait == WAIT_OBJECT_0)
ReleaseMutex( mutex );
CloseHandle( mutex );
}
#else
OutputDebugString( szFormat );
#endif
}
#endif

View File

@ -43,17 +43,16 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
LPVOID mem;
DWORD mem32;
#define CODESIZE 20
BYTE code[CODESIZE+MAX_PATH*sizeof(TCHAR)];
BYTE code[CODESIZE+TSIZE(MAX_PATH)];
union
{
PBYTE pB;
PDWORD pL;
} ip;
len = lstrlen( dll ) + 1;
if (len > MAX_PATH)
len = TSIZE(lstrlen( dll ) + 1);
if (len > TSIZE(MAX_PATH))
return;
len *= sizeof(TCHAR);
if (LLW == 0)
{
@ -66,7 +65,7 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
// Assume if one is defined, so is the other.
if (Wow64GetThreadContext == 0)
{
DEBUGSTR( L"Failed to get pointer to Wow64GetThreadContext.\n" );
DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext.\n" );
return;
}
#endif
@ -76,13 +75,13 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
// ...ANSI32.dll\0
CopyMemory( code, dll, len - 7*sizeof(TCHAR) );
CopyMemory( code, dll, len - TSIZE(7) );
// ...ANSI-LLW.exe\0
CopyMemory( code + len - 7*sizeof(TCHAR), L"-LLW.exe", 9*sizeof(TCHAR) );
CopyMemory( code + len - TSIZE(7), L"-LLW.exe", TSIZE(9) );
if (!CreateProcess( (LPCTSTR)code, NULL, NULL, NULL, FALSE, 0, NULL, NULL,
&si, &pi ))
{
DEBUGSTR( L"Failed to execute \"%s\".\n", (LPCTSTR)code );
DEBUGSTR( 1, L"Failed to execute \"%s\".\n", (LPCTSTR)code );
return;
}
WaitForSingleObject( pi.hProcess, INFINITE );

View File

@ -29,7 +29,7 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
PDWORD64 pL;
} ip;
#define CODESIZE 92
static BYTE code[CODESIZE+MAX_PATH*sizeof(TCHAR)] = {
static BYTE code[CODESIZE+TSIZE(MAX_PATH)] = {
0,0,0,0,0,0,0,0, // original rip
0,0,0,0,0,0,0,0, // LoadLibraryW
0x9C, // pushfq
@ -72,10 +72,9 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
0, // dword alignment for LLW, fwiw
};
len = lstrlen( dll ) + 1;
if (len > MAX_PATH)
len = TSIZE(lstrlen( dll ) + 1);
if (len > TSIZE(MAX_PATH))
return;
len *= sizeof(TCHAR);
CopyMemory( code + CODESIZE, dll, len );
len += CODESIZE;

View File

@ -1,29 +1,32 @@
# 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
# I've used TDM64 (gcc 4.6.1), building the 32-bit version in the x86 directory
# and the 64-bit version in the x64 directory. MinGW32 (gcc 3.4.5) will also
# build the 32-bit version, but will of course fail on the 64-bit.
# build the 32-bit version.
# 19 November, 2010:
# explicitly use 64-bit flags, in case the compiler isn't.
#
# 13 December, 2011:
# use CMD for file operations, not programs from fileutils.
CC = gcc
CFLAGS = -O2 -Wall
X86OBJS = x86/proctype.o x86/injdll32.o x86/debugstr.o
X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/debugstr.o
X86OBJS = x86/proctype.o x86/injdll32.o x86/util.o
X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/util.o
x86/%.o: %.c ansicon.h
$(CC) -m32 -c $(CFLAGS) $(CPPFLAGS) $< -o $@
$(CC) -m32 -c $(CFLAGS) $< -o $@
x86/%v.o: %.rc
x86/%v.o: %.rc version.h
windres -U _WIN64 -F pe-i386 $< $@
x64/%.o: %.c ansicon.h
$(CC) -m64 -c $(CFLAGS) $(CPPFLAGS) $< -o $@
$(CC) -m64 -c $(CFLAGS) $< -o $@
x64/%v.o: %.rc
x64/%v.o: %.rc version.h
windres -F pe-x86-64 $< $@
all: ansicon32 ansicon64
@ -33,34 +36,40 @@ ansicon32: x86 x86/ansicon.exe x86/ANSI32.dll
ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll x64/ANSI-LLW.exe
x86:
mkdir x86
cmd /c "mkdir x86"
x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o
$(CC) -m32 $+ -s -o $@ -lpsapi -lole32
$(CC) -m32 $+ -s -o $@
x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o
$(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared
x64:
mkdir x64
cmd /c "mkdir x64"
x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o
$(CC) -m64 $+ -s -o $@ -lpsapi -lole32
$(CC) -m64 $+ -s -o $@
x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o
$(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared
x64/ANSI32.dll: x86/ANSI32.dll
cp -p x86/ANSI32.dll x64/ANSI32.dll
cmd /c "copy/y x86\ANSI32.dll x64\ANSI32.dll >nul"
x64/ANSI-LLW.exe: ANSI-LLW.c
$(CC) -m32 $(CFLAGS) $< -s -o $@
x86/ansicon.o: version.h
x86/ANSI.o: version.h
x64/ansicon.o: version.h
x64/ANSI.o: version.h
x86/ansiconv.o: ansicon.rc
x86/ansiv.o: ansi.rc
x64/ansiconv.o: ansicon.rc
x64/ansiv.o: ansi.rc
# Need two commands, because if the directory doesn't exist, it won't delete
# anything at all.
clean:
-rm x86/*.o
-rm x64/*.o
-cmd /c "del x86\*.o 2>nul"
-cmd /c "del x64\*.o 2>nul"

View File

@ -26,10 +26,13 @@ DIR = x64
CC = cl
CFLAGS = /nologo /W3 /Ox /GF /D_CRT_SECURE_NO_WARNINGS
LIBS = advapi32.lib shell32.lib user32.lib psapi.lib ole32.lib
LIBS = advapi32.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
# This is required for the 2003 Platform SDK, but not for Visual Studio 2010.
#LIBS64 = bufferoverflowu.lib
X86OBJS = x86\proctype.obj x86\injdll32.obj x86\util.obj
X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj
{}.c{$(DIR)}.obj:
$(CC) /c $(CFLAGS) /Fo$@ $<
@ -58,20 +61,22 @@ x64:
mkdir x64
x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res
$(CC) /nologo /Fe$@ $** $(LIBS) bufferoverflowu.lib
$(CC) /nologo /Fe$@ $** $(LIBS) $(LIBS64)
x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res
$(CC) /nologo /LD /Fe$@ $** $(LIBS) bufferoverflowu.lib
$(CC) /nologo /LD /Fe$@ $** $(LIBS) $(LIBS64)
x64\ANSI32.dll: x86\ANSI32.dll
copy x86\ANSI32.dll x64\ANSI32.dll
x64\ANSI-LLW.exe: ANSI-LLW.c
$(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? bufferoverflowu.lib
$(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? $(LIBS64)
ansicon.c: ansicon.h
ANSI.c: ansicon.h
debugstr.c: ansicon.h
ansicon.c: ansicon.h version.h
ansicon.rc: version.h
ANSI.c: ansicon.h version.h
ANSI.rc: version.h
util.c: ansicon.h
injdll32.c: ansicon.h
injdll64.c: ansicon.h
proctype.c: ansicon.h

View File

@ -8,67 +8,75 @@
hardly seems worth it. There's GetModuleInformation, but passing in NULL just
returns a base of NULL, so that's no help. Since 64/32 is sufficient, let
ansicon.exe handle the difference between console/GUI.
Update: ignore images characterised as DLL.
*/
#include "ansicon.h"
int ProcessType( LPPROCESS_INFORMATION pinfo )
int ProcessType( LPPROCESS_INFORMATION pinfo, BOOL* gui )
{
char* ptr;
MEMORY_BASIC_INFORMATION minfo;
char* ptr = 0;
IMAGE_DOS_HEADER dos_header;
IMAGE_NT_HEADERS nt_header;
SIZE_T read;
while (VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) ))
*gui = FALSE;
for (ptr = NULL;
VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) );
ptr += minfo.RegionSize)
{
IMAGE_DOS_HEADER dos_header;
SIZE_T read;
if (minfo.BaseAddress == minfo.AllocationBase &&
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.Signature == IMAGE_NT_SIGNATURE &&
(nt_header.FileHeader.Characteristics &
(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL))
== IMAGE_FILE_EXECUTABLE_IMAGE)
{
BOOL gui = (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI);
if (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
gui )
*gui = (nt_header.OptionalHeader.Subsystem
== IMAGE_SUBSYSTEM_WINDOWS_GUI);
if (nt_header.OptionalHeader.Subsystem ==
IMAGE_SUBSYSTEM_WINDOWS_CUI || *gui)
{
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
{
DEBUGSTR( L" %p: 32-bit %s",
minfo.AllocationBase, (gui) ? L"GUI" : L"console" );
DEBUGSTR( 1, L" 32-bit %s (base = %p)",
(*gui) ? L"GUI" : L"console", minfo.AllocationBase );
return 32;
}
#ifdef _WIN64
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
{
DEBUGSTR( L" %p: 64-bit %s",
minfo.AllocationBase, (gui) ? L"GUI" : L"console" );
DEBUGSTR( 1, L" 64-bit %s (base = %p)",
(*gui) ? L"GUI" : L"console", minfo.AllocationBase );
return 64;
}
#endif
DEBUGSTR( L" Ignoring unsupported machine (0x%X)",
DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)",
nt_header.FileHeader.Machine );
}
else
{
DEBUGSTR( L" Ignoring non-Windows subsystem (%u)",
DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)",
nt_header.OptionalHeader.Subsystem );
}
return 0;
}
}
return 0;
}
}
ptr += minfo.RegionSize;
}
DEBUGSTR( L" Ignoring non-Windows process" );
DEBUGSTR( 1, L" Ignoring non-Windows process" );
return 0;
}

View File

@ -3,7 +3,7 @@
Copyright 2005-2011 Jason Hood
Version 1.40. Freeware
Version 1.50. Freeware
===========
@ -30,9 +30,9 @@
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.
currently using it, running with `-u' (and/or `-U') to remove the Auto-
Run entry/ies, then removing the directory from PATH or deleting the
files. No other changes are made.
---------
Upgrading
@ -40,6 +40,7 @@
Delete ANSI.dll, it has been replaced with ANSI32.dll.
Delete ANSI-LLA.dll, it has been replaced with ANSI-LLW.dll.
Uninstall with your current version and install with this version.
=====
@ -48,6 +49,8 @@
Options (case sensitive):
-l Log to %temp%\ansicon.log.
-p Enable the parent process (i.e. the command shell used to
run ANSICON) to recognise escapes.
@ -55,7 +58,7 @@
("monochrome"), or the attribute following the `m' (please
use `COLOR /?' for attribute values).
-e Echo the command line - the character after the `e' is
-e Echo the command line - a space or tab after the `e' is
ignored, the remainder is displayed verbatim.
-E As above, but no newline is added.
@ -74,12 +77,46 @@
Eg: `ansicon -m30 -t file.ans' will display `file.ans' using black on
cyan as the default color.
The attribute may start with "-" to permanently reverse the foreground
and background colors (but not when using `-p'). Eg: `ansicon -m-f0 -t
file.log' will use reversed black on white as the default (i.e. white on
black, with foreground sequences changing the background).
If you experience trouble with certain programs, the log may help in
finding the cause; it can be found at "%TEMP%\ansicon.log". A number
should follow the `l':
0 No logging
1 Log process start and end
2 Above, plus log modules used by the process
3 Above, plus log functions that are hooked
4 Log console output (add to any of the above)
8 Append to the existing file (add to any of the above)
The log option will not work with `-p'; set the environment variable
ANSICON_LOG instead. The variable is only read once when a new process
is started; changing it won't affect running processes. If you identify
a module that causes problems (one known is "nvd3d9wrap.dll") add it to
the ANSICON_EXC environment variable (see ANSICON_API below, but the
extension is required).
Once installed, the ANSICON environment variable will be created. This
variable is of the form "WxH (wxh)", where W & H are the width and
height of the buffer and w & h are the width and height of the window.
The variable is updated whenever a program reads it directly (i.e. as
an individual request, not as part of the entire environment block).
For example, "set an" will not update it, but "echo %ansicon%" will.
Also created is ANSICON_VER, which contains the version without the
point (1.50 becomes "150"). This variable does not exist as part of the
environment block ("set an" will not show it).
If installed, GUI programs will not be hooked. Either start the program
directly with `ansicon', or add it to the ANSICON_GUI variable (see
ANSICON_API below).
Using `ansicon' after install will always start with the default attrib-
utes, restoring the originals on exit; all other programs will use the
current attributes. The shift state is always reset for a new process.
The Windows API WriteFile and WriteConsoleA functions will set the num-
ber of characters written, not the number of bytes. When using a multi-
@ -142,7 +179,10 @@
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.
underline attributes, whilst the latter will explicitly reset them. The
environment variable ANSICON_DEF can be used to change the default col-
ors (same value as `-m'; setting the variable does not change the cur-
rent colors).
=================
@ -217,8 +257,6 @@
The entire console buffer is used, not just the visible window.
If running CMD.EXE, its own COLOR will be the initial color.
The 64-bit version can inject into a 32-bit process, but the 32-bit
version will not inject into a 64-bit process.
@ -231,6 +269,19 @@
Legend: + added, - bug-fixed, * changed.
1.50 - 14 December, 2011:
- -u does not imply -p;
- return the program's exit code;
- -p by itself will not restore original color;
- output error messages to stderr;
* logging is always available, with various levels; include the pid;
* don't automatically hook GUI programs, use `ansicon' or ANSICON_GUI;
* always place first in AutoRun; don't run if already installed;
+ global reverse video capability;
+ added ANSICON_VER to provide version/install test;
+ added ANSICON_EXC to exclude selected modules;
+ added ANSICON_DEF to explicitly set the default SGM.
1.40 - 1 March, 2011:
- hook GetProcAddress (now PowerShell works);
+ add SO/SI, using the DEC Special Graphics Character Set for G1;
@ -364,5 +415,5 @@
in the version text and a source diff is included.
==========================
Jason Hood, 3 March, 2011.
==============================
Jason Hood, 14 December, 2011.

125
util.c Normal file
View File

@ -0,0 +1,125 @@
/*
util.c - Utility functions.
*/
#include "ansicon.h"
TCHAR prog_path[MAX_PATH];
LPTSTR prog;
int log_level;
char tempfile[MAX_PATH];
DWORD pid;
// Get just the name of the program: "C:\path\program.exe" -> "program".
// Returns a pointer within program; it is modified to remove the extension.
LPTSTR get_program_name( LPTSTR program )
{
LPTSTR name, ext;
if (program == NULL)
{
GetModuleFileName( NULL, prog_path, lenof(prog_path) );
program = prog_path;
}
name = wcsrchr( program, '\\' );
if (name != NULL)
++name;
else
name = program;
ext = wcsrchr( name, '.' );
if (ext != NULL && ext != name)
*ext = '\0';
return name;
}
void DEBUGSTR( int level, LPTSTR szFormat, ... )
{
TCHAR szBuffer[1024], szEscape[1024];
va_list pArgList;
HANDLE mutex;
DWORD wait;
FILE* file;
if ((log_level & 3) < level && !(level & 4 & log_level))
return;
if (*tempfile == '\0')
{
_snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) );
pid = GetCurrentProcessId();
}
if (szFormat == NULL)
{
file = fopen( tempfile, "wt" );
if (file != NULL)
{
SYSTEMTIME now;
GetLocalTime( &now );
fprintf( file, "Logging started %d-%.2d-%.2d %d:%.2d:%.2d\n",
now.wYear, now.wMonth, now.wDay,
now.wHour, now.wMinute, now.wSecond );
fclose( file );
}
return;
}
va_start( pArgList, szFormat );
_vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList );
va_end( pArgList );
szFormat = szBuffer;
if (*szFormat == '\33')
{
BOOL first = TRUE;
LPTSTR pos = szEscape;
while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4)
{
if (*szFormat < 32)
{
*pos++ = '\\';
switch (*szFormat)
{
case '\a': *pos++ = 'a'; break;
case '\b': *pos++ = 'b'; break;
case '\t': *pos++ = 't'; break;
case '\r': *pos++ = 'r'; break;
case '\n': *pos++ = 'n'; break;
case 27 : *pos++ = 'e'; break;
default:
pos += _snwprintf( pos, 32, L"%.*o",
(szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1,
*szFormat );
}
}
else
{
if (*szFormat == '"')
{
if (first)
first = FALSE;
else if (szFormat[1] != '\0')
*pos++ = '\\';
}
*pos++ = *szFormat;
}
}
*pos = '\0';
szFormat = szEscape;
}
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
wait = WaitForSingleObject( mutex, 500 );
file = fopen( tempfile, "at" ); // _fmode might be binary
if (file != NULL)
{
fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat );
fclose( file );
}
if (wait == WAIT_OBJECT_0)
ReleaseMutex( mutex );
CloseHandle( mutex );
}

9
version.h Normal file
View File

@ -0,0 +1,9 @@
/*
version.h - Version defines.
*/
#define PVERS L"1.50" // wide string
#define PVERSA "1.50" // ANSI string (windres 2.16.91 didn't like L)
#define PVERE L"150" // wide environment string
#define PVEREA "150" // ANSI environment string
#define PVERB 1,5,0,0 // binary (resource)