Compare commits

..

10 Commits

Author SHA1 Message Date
Jason Hood
37f92009f6 Release 1.89. 2019-04-29 20:20:34 +10:00
Jason Hood
0fc890dddd Prevent occasional freeze on startup
If the console window has a full eight-digit handle my custom printf
would get stuck in a loop, causing CMD to seemingly freeze.  Do what I
really should have done in the first place and make it more robust.
2019-04-29 20:13:07 +10:00
Jason Hood
0dd40f6d74 Simplify getting buffer info 2019-04-29 20:11:00 +10:00
Jason Hood
d7a2d5e962 Fix ANSICON environment variable
Creating a console handle in `set_ansicon` was assumed to succeed, but
that is not the case when there is no console (if the process was
started detached or freed its console).  This left the console info
uninitialised, causing my `printf` replacement to get stuck in a loop.

Resolves #127.
2019-03-01 17:47:05 +10:00
Jason Hood
a1bf74dff9 Fix crash when some programs start
A few functions that are hooked are not imported, so testing if my
import was already hooked would fail.

SetCurrentConsoleFontEx should have been hooked, but wasn't.
2019-02-03 18:49:28 +10:00
Jason Hood
eccbbe7a54 Fixes
Unhook on terminate, to properly exit (Vista CMD.EXE and a MinGW-built
ansicon.exe would seem to exit okay, but the exit code was wrong).
Fixes #123.

Verify the DLL exists prior to injection.  With the DLL now being added
to the import table, failing to find it would prevent the process itself
from loading.
2018-11-04 11:17:23 +10:00
Jason Hood
0472db80b2 Preserve last error
Hooked functions that failed and logged debug messages were causing an
incorrect last error to be set.
2018-08-23 17:38:43 +10:00
Jason Hood
06459edb69 Log CreateFile calls
Since I've hooked CreateFile use log level 32 to log how it's used,
providing a simple file monitor.
2018-08-23 17:34:34 +10:00
Jason Hood
4e84582f02 Fix running directly via ansicon
A program run directly by `ansicon` should always be hooked, regardless
if it's GUI or excluded.  This was broken in v1.84 due to the DLL being
imported.
2018-08-23 17:26:52 +10:00
Jason Hood
eec487abb6 Prevent -p injecting when already injected
Don't inject into the parent if injection has already occurred.  This
prevents the reference count from increasing, allowing a single -pu to
unload, rather than one -pu for each -p.
2018-08-23 17:06:49 +10:00
7 changed files with 212 additions and 67 deletions

168
ANSI.c
View File

@ -210,8 +210,25 @@
scrolling will use the default attribute for new lines;
workaround Windows 10 1803 console bug.
v1.85, 22 August, 2018:
fix creating the wrap buffer.
v1.85, 22 & 23 August, 2018:
fix creating the wrap buffer;
always inject from ansicon.exe, even if it's GUI or excluded;
log CreateFile calls;
preserve last error.
v1.86, 4 November, 2018:
always unhook, even on terminate;
check the DLL still exists before adding to imports.
v1.87, 3 February, 2019:
some hooked functions are not imported, so myimport wasn't set;
add missing SetCurrentConsoleFontEx to list of hooks.
v1.88, 1 March, 2019:
a detached process has no console handle (fixes set_ansicon).
v1.89, 29 April, 2019:
an eight-digit window handle would break my custom printf.
*/
#include "ansicon.h"
@ -236,6 +253,7 @@ DWORD orgmode; // original mode
CONSOLE_CURSOR_INFO orgcci; // original cursor state
HANDLE hHeap; // local memory heap
HANDLE hBell, hFlush;
BOOL ansicon; // are we in ansicon.exe?
#define CACHE 5
struct Cache
@ -524,26 +542,14 @@ void get_state( void )
csbix.cbSize = sizeof(csbix);
if (GetConsoleScreenBufferInfoX( hConOut, &csbix ))
{
Info.dwSize = csbix.dwSize;
ATTR = csbix.wAttributes;
WIN = csbix.srWindow;
arrcpy( pState->o_palette, csbix.ColorTable );
ATTR = csbix.wAttributes;
}
else
{
arrcpy( pState->o_palette, legacy_palette );
if (!GetConsoleScreenBufferInfo( hConOut, &Info ))
{
DEBUGSTR( 1, "Failed to get screen buffer info (%u) - assuming defaults",
GetLastError() );
ATTR = 7;
WIDTH = 80;
HEIGHT = 300;
WIN.Left = 0;
WIN.Right = 79;
TOP = 0;
BOTTOM = 24;
}
ATTR = 7;
}
arrcpy( pState->x_palette, xterm_palette );
@ -563,7 +569,10 @@ void get_state( void )
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if (!GetConsoleScreenBufferInfo( hConOut, &Info ))
{
RtlZeroMemory( &Info, sizeof(Info) );
ATTR = 7;
}
if (pState->sgr.reverse)
{
*a++ = '-';
@ -2794,6 +2803,10 @@ BOOL HookAPIOneMod(
hook->myimport = &pThunk->u1.Function;
DEBUGSTR( 3, " %s%s", sp, hook->name );
}
else if (hook->myimport == 0)
{
patch = hook->newfunc;
}
else
{
// Don't hook if our import already points to the module being
@ -3003,7 +3016,7 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
name = get_program( app, child_pi->hProcess, wide, lpApp, lpCmd );
DEBUGSTR( 1, "%S (%u)", name, child_pi->dwProcessId );
if (search_env( L"ANSICON_EXC", name ))
if (!ansicon && search_env( L"ANSICON_EXC", name ))
{
DEBUGSTR( 1, " Excluded" );
type = 0;
@ -3011,7 +3024,7 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
else
{
type = ProcessType( child_pi, &base, &gui );
if (gui && type > 0)
if (!ansicon && gui && type > 0)
{
if (!search_env( L"ANSICON_GUI", name ))
{
@ -3022,6 +3035,17 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
}
if (type > 0)
{
#if defined(_WIN64) || defined(W32ON64)
if (type == 32)
*(PDWORD)DllNameType = 0x320033/*L'23'*/;
else
*(PDWORD)DllNameType = 0x340036/*L'46'*/;
#endif
if (GetFileAttributes( DllName ) == INVALID_FILE_ATTRIBUTES)
type = 0;
}
if (type > 0)
{
#ifdef _WIN64
if (type == 64)
{
@ -3110,7 +3134,9 @@ BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName,
lpStartupInfo,
&child_pi ))
{
DEBUGSTR( 1, " Failed (%u)", GetLastError() );
DWORD err = GetLastError();
DEBUGSTR( 1, " Failed (%u)", err );
SetLastError( err );
return FALSE;
}
@ -3148,7 +3174,9 @@ BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName,
lpStartupInfo,
&child_pi ))
{
DEBUGSTR( 1, " Failed (%u)", GetLastError() );
DWORD err = GetLastError();
DEBUGSTR( 1, " Failed (%u)", err );
SetLastError( err );
return FALSE;
}
@ -3219,8 +3247,10 @@ FARPROC WINAPI MyGetProcAddress( HMODULE hModule, LPCSTR lpProcName )
HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName )
{
HMODULE hMod = LoadLibraryA( lpFileName );
DEBUGSTR( 2, "LoadLibraryA %s", lpFileName );
DWORD err = GetLastError();
DEBUGSTR( 2, "LoadLibraryA %\"s", lpFileName );
HookAPIAllMod( Hooks, FALSE, TRUE );
SetLastError( err );
return hMod;
}
@ -3228,8 +3258,10 @@ HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName )
HMODULE WINAPI MyLoadLibraryW( LPCWSTR lpFileName )
{
HMODULE hMod = LoadLibraryW( lpFileName );
DEBUGSTR( 2, "LoadLibraryW %S", lpFileName );
DWORD err = GetLastError();
DEBUGSTR( 2, "LoadLibraryW %\"S", lpFileName );
HookAPIAllMod( Hooks, FALSE, TRUE );
SetLastError( err );
return hMod;
}
@ -3242,8 +3274,10 @@ HMODULE WINAPI MyLoadLibraryExA( LPCSTR lpFileName, HANDLE hFile,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE |
LOAD_LIBRARY_AS_IMAGE_RESOURCE)))
{
DEBUGSTR( 2, "LoadLibraryExA %s", lpFileName );
DWORD err = GetLastError();
DEBUGSTR( 2, "LoadLibraryExA %\"s", lpFileName );
HookAPIAllMod( Hooks, FALSE, TRUE );
SetLastError( err );
}
return hMod;
}
@ -3257,8 +3291,10 @@ HMODULE WINAPI MyLoadLibraryExW( LPCWSTR lpFileName, HANDLE hFile,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE |
LOAD_LIBRARY_AS_IMAGE_RESOURCE)))
{
DEBUGSTR( 2, "LoadLibraryExW %S", lpFileName );
DWORD err = GetLastError();
DEBUGSTR( 2, "LoadLibraryExW %\"S", lpFileName );
HookAPIAllMod( Hooks, FALSE, TRUE );
SetLastError( err );
}
return hMod;
}
@ -3612,6 +3648,55 @@ WINAPI MyFreeLibrary( HMODULE hModule )
// Add GENERIC_READ access to enable retrieving console info.
//-----------------------------------------------------------------------------
static void log_CreateFile( HANDLE h, LPCVOID name, BOOL wide, DWORD access,
DWORD dwDesiredAccess, DWORD dwCreationDisposition )
{
DWORD err = GetLastError();
static char log[] = "CreateFile%s: %*s, %s, %s, %\"s";
LPCSTR acc, op;
char state[32];
int len;
if (access != dwDesiredAccess)
acc = "w->r/w";
else if (access == (GENERIC_READ | GENERIC_WRITE) ||
(access & (FILE_READ_DATA | FILE_WRITE_DATA)) == (FILE_READ_DATA |
FILE_WRITE_DATA))
acc = "r/w";
else if (access == GENERIC_WRITE ||
access & (FILE_WRITE_DATA | FILE_APPEND_DATA))
acc = "write";
else if (access == GENERIC_READ ||
access & FILE_READ_DATA)
acc = "read";
else
acc = "access";
switch (dwCreationDisposition)
{
case CREATE_ALWAYS: op = "create"; break;
case CREATE_NEW: op = "new"; break;
case OPEN_ALWAYS: op = "open"; break;
case OPEN_EXISTING: op = "existing"; break;
case TRUNCATE_EXISTING: op = "truncate"; break;
default: op = "unknown"; break;
}
if (h == INVALID_HANDLE_VALUE)
len = ac_sprintf( state, "failed (%u)", err );
else
{
state[0] = 'o';
state[1] = 'k';
len = 2;
}
log[sizeof(log) - 2] = wide ? 'S' : 's';
DEBUGSTR( 1, log, wide ? "W" : "A", len, state, op, acc, name );
SetLastError( err );
}
HANDLE
WINAPI MyCreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess,
DWORD dwShareMode,
@ -3619,6 +3704,10 @@ WINAPI MyCreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile )
{
LPCSTR name = lpFileName;
DWORD access = dwDesiredAccess;
HANDLE h;
if (dwDesiredAccess == GENERIC_WRITE)
{
PDWORD con = (PDWORD)lpFileName;
@ -3629,9 +3718,13 @@ WINAPI MyCreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess,
dwDesiredAccess |= GENERIC_READ;
}
}
return CreateFileA( lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile );
h = CreateFileA( lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile );
if (log_level & 32)
log_CreateFile( h, name, FALSE, access,
dwDesiredAccess, dwCreationDisposition );
return h;
}
HANDLE
@ -3641,6 +3734,10 @@ WINAPI MyCreateFileW( LPCWSTR lpFileName, DWORD dwDesiredAccess,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile )
{
LPCWSTR name = lpFileName;
DWORD access = dwDesiredAccess;
HANDLE h;
if (dwDesiredAccess == GENERIC_WRITE)
{
#ifdef _WIN64
@ -3660,9 +3757,13 @@ WINAPI MyCreateFileW( LPCWSTR lpFileName, DWORD dwDesiredAccess,
dwDesiredAccess |= GENERIC_READ;
}
}
return CreateFileW( lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile );
h = CreateFileW( lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile );
if (log_level & 32)
log_CreateFile( h, name, TRUE, access,
dwDesiredAccess, dwCreationDisposition );
return h;
}
HANDLE
@ -3770,7 +3871,8 @@ void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi )
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
GetConsoleScreenBufferInfo( hConOut, &csbi );
if (!GetConsoleScreenBufferInfo( hConOut, &csbi ))
RtlZeroMemory( &csbi, sizeof(csbi) );
CloseHandle( hConOut );
pcsbi = &csbi;
}
@ -3883,6 +3985,7 @@ HookFn Hooks[] = {
HOOK( APIConsole, SetConsoleScreenBufferSize ),
HOOK( APIConsole, SetConsoleTextAttribute ),
HOOK( APIConsole, SetConsoleWindowInfo ),
HOOK( APIConsole, SetCurrentConsoleFontEx ),
HOOK( APIConsole, WriteConsoleOutputA ),
HOOK( APIConsole, WriteConsoleOutputW ),
HOOK( APIConsole, WriteConsoleOutputAttribute ),
@ -3927,6 +4030,7 @@ void OriginalAttr( PVOID lpReserved )
else
{
// We also want to restore the original attributes for ansicon.exe.
ansicon =
org = (pNTHeader->OptionalHeader.MajorImageVersion == 20033 && // 'AN'
pNTHeader->OptionalHeader.MinorImageVersion == 18771); // 'SI'
}
@ -4040,7 +4144,6 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
if (lpReserved == NULL)
{
DEBUGSTR( 1, "Unloading" );
HookAPIAllMod( Hooks, TRUE, FALSE );
if (winmm != NULL)
FreeLibrary( winmm );
}
@ -4048,6 +4151,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
{
DEBUGSTR( 1, "Terminating" );
}
HookAPIAllMod( Hooks, TRUE, FALSE );
if (orgattr != 0)
{
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,

View File

@ -1,4 +1,4 @@
Copyright (C) 2005-2018 Jason Hood
Copyright (C) 2005-2019 Jason Hood
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages

View File

@ -93,11 +93,13 @@
v1.84, 7 May, 2018:
import the DLL.
v1.85, 22 August, 2018:
use IsConsoleHandle for my_fputws, to distinguish NUL.
v1.85, 22 & 23 August, 2018:
use IsConsoleHandle for my_fputws, to distinguish NUL;
don't load into the parent if already loaded;
add log level 32 to log CreateFile.
*/
#define PDATE L"22 August, 2018"
#define PDATE L"29 April, 2019"
#include "ansicon.h"
#include "version.h"
@ -219,10 +221,10 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0)
{
proc = me.modBaseAddr;
if (!unload || param)
if (param)
break;
}
else if (unload)
else
{
#ifdef _WIN64
if (_wcsicmp( me.szModule, DllNameType - 4 ) == 0)
@ -247,6 +249,11 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
DEBUGSTR( 1, " Unable to locate ANSICON's DLL" );
return;
}
else if (!unload && param != NULL)
{
DEBUGSTR( 1, " ANSICON already loaded" );
return;
}
#ifdef _WIN64
rva = GetProcRVA( L"kernel32.dll", (unload) ? "FreeLibrary"
@ -864,25 +871,25 @@ 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[u]]\n"
L" [-e|E string | -t|T [file(s)] | program [args]]\n"
L"ansicon [-lLEVEL] [-i] [-I] [-u] [-U] [-m[ATTR]] [-p[u]]\n"
L" [-e|E STRING | -t|T [FILE...] | 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" \t\t +4=output, +8=append, +16=imports, +32=files) for PROGRAM\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" -m\t\tuse grey on black (\"monochrome\") or ATTR as default color\n"
L" -p\t\thook into the parent process\n"
L" -pu\t\tunhook from the parent process\n"
L" -e\t\techo string\n"
L" -E\t\techo string, don't append newline\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" 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"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

@ -23,6 +23,9 @@
#include <stdio.h>
#include <stdlib.h>
#ifndef INVALID_FILE_ATTRIBUTES
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif
#ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
#endif

View File

@ -1,9 +1,9 @@
ANSICON
Copyright 2005-2018 Jason Hood
Copyright 2005-2019 Jason Hood
Version 1.85. Freeware
Version 1.89. Freeware
Description
@ -102,6 +102,7 @@ Usage
4 Log console output (add to any of the above)
8 Append to the existing file (add to any of the above)
16 Log all imported modules (add to any of the above)
32 Log CreateFile (add to any of the above)
The log option will not work with '-p'; set the environment variable
ANSICON_LOG (to the number) instead. The variable is only read once when a
@ -339,9 +340,27 @@ Version History
Legend: + added, - bug-fixed, * changed.
1.85 - 22 August, 2018:
1.89 - 29 April, 2019:
- fix occasional freeze on startup (bug converting 8-digit window handle).
1.88 - 1 March, 2019:
- fix ANSICON environment variable when there is no console.
1.87 - 3 February, 2019:
- fix crash when some programs start (bug during hooking);
- properly hook SetCurrentConsoleFontEx.
1.86 - 4 November, 2018:
- check the DLL exists before importing it (allows renaming to update);
- unhook on terminate, as well (fixes issues with Vista and MinGW).
1.85 - 23 August, 2018:
- fix wrap issues with a buffer bigger than the window;
- fix -e et al when redirecting to NUL.
- fix -e et al when redirecting to NUL;
- prevent -p from injecting when already injected;
- fix running directly via ansicon (hook even if it's GUI or excluded);
- preserve last error;
+ add log level 32 to monitor CreateFile.
1.84 - 11 May, 2018:
- close the flush handles on detach;
@ -631,5 +650,5 @@ Distribution
in LICENSE.txt.
============================
Jason Hood, 22 August, 2018.
===========================
Jason Hood, 29 April, 2019.

32
util.c
View File

@ -593,7 +593,7 @@ int ac_sprintf( char* buf, const char* fmt, ... )
}
do
{
*buf++ = (unsigned)(num / power) % 10 + '0';
*buf++ = num / power % 10 + '0';
power /= 10;
} while (power);
}
@ -609,7 +609,7 @@ int ac_sprintf( char* buf, const char* fmt, ... )
// * BUF is big enough;
// * FMT is valid;
// * width is only for X, is only 2 and the number is not bigger than that;
// * X, d & u are 32-bit unsigned decimal, a digit less than maximum;
// * X, d & u are 32-bit unsigned decimal;
// * c is not output if NUL;
// * no other type is used;
// * return value is not used.
@ -638,9 +638,15 @@ int ac_wprintf( wchar_t* buf, const char* fmt, ... )
}
else if (t == 'X')
{
int bits = 4;
while (num >> bits)
bits += 4;
int bits;
if (num & 0xF0000000)
bits = 32;
else
{
bits = 4;
while (num >> bits)
bits += 4;
}
do
{
bits -= 4;
@ -654,14 +660,20 @@ int ac_wprintf( wchar_t* buf, const char* fmt, ... )
}
else // (t == 'd' || t == 'u')
{
int power = 10;
while (num / power)
power *= 10;
unsigned power;
if (num >= 1000000000)
power = 1000000000;
else
{
power = 1;
while (num / (power * 10))
power *= 10;
}
do
{
*buf++ = num / power % 10 + '0';
power /= 10;
*buf++ = (int)(num / power) % 10 + '0';
} while (power != 1);
} while (power);
}
}
}

View File

@ -2,11 +2,11 @@
version.h - Version defines.
*/
#define PVERS L"1.85" // wide string
#define PVERSA "1.85" // ANSI string (windres 2.16.91 didn't like L)
#define PVERE L"185" // wide environment string
#define PVEREA "185" // ANSI environment string
#define PVERB 1,8,5,0 // binary (resource)
#define PVERS L"1.89" // wide string
#define PVERSA "1.89" // ANSI string (windres 2.16.91 didn't like L)
#define PVERE L"189" // wide environment string
#define PVEREA "189" // ANSI environment string
#define PVERB 1,8,9,0 // binary (resource)
#ifdef _WIN64
# define BITS L"64"