Remove dependence on the CRT; import DLL; fixes

Windows 10's MSVCRT will only work if the Win32 version in the header is
0 or 10.  Some PE's use it for something else, so when the DLL is
injected the process fails.  Provide custom routines for the C functions
used, so the DLL only depends on KERNEL32.

With the DLL independent of the CRT that would mean the exe would either
also need to be independent, or the source files would need to be built
twice (or just remove a linker warning).  Another option is to export
the functions from the DLL and have the exe import them, which turned
out to simplify things quite nicely.

A process that has a really long command line would not log properly, so
double the heap to accommodate it.

If ANSICON_DEF could not be parsed the default attribute would be zero
(black on black).  Use 7 or -7 instead.
This commit is contained in:
Jason Hood 2018-05-07 10:31:41 +10:00
parent f8509c916c
commit 33ba31ad3c
9 changed files with 579 additions and 312 deletions

264
ANSI.c
View File

@ -197,14 +197,17 @@
v1.83, 16 February, 2018: v1.83, 16 February, 2018:
create the flush thread on first use. create the flush thread on first use.
v1.84-wip, 17 February, 26 April to 4 May, 2018: v1.84-wip, 17 February, 26 April to 7 May, 2018:
close the flush handles on detach; close the flush handles on detach;
dynamically load WINMM.DLL; dynamically load WINMM.DLL;
use sprintf/_snprintf/_snwprintf instead of wsprintf, avoiding USER32.DLL; use sprintf/_snprintf/_snwprintf instead of wsprintf, avoiding USER32.DLL;
replace bsearch (in procrva.c) with specific code; replace bsearch (in procrva.c) with specific code;
if the primary thread is detached exit the process; if the primary thread is detached exit the process;
get real WriteFile handle before testing for console; get real WriteFile handle before testing for console;
use remote load on Win8+ when the process has no IAT. use remote load on Win8+ when the process has no IAT;
remove dependency on the CRT;
increase heap to 256KiB to fix logging of really long command lines;
default to 7 or -7 if ANSICON_DEF could not be parsed.
*/ */
#include "ansicon.h" #include "ansicon.h"
@ -445,6 +448,8 @@ typedef struct
BYTE reverse; // swap console foreground & background attributes BYTE reverse; // swap console foreground & background attributes
} SGR; } SGR;
SGR orgsgr; // original SGR
typedef struct typedef struct
{ {
SGR sgr, SaveSgr; SGR sgr, SaveSgr;
@ -493,7 +498,7 @@ void get_state( void )
valid_state = TRUE; valid_state = TRUE;
_snwprintf( buf, lenof(buf), L"ANSICON_State_%X", PtrToUint( hwnd ) ); ac_wprintf( buf, "ANSICON_State_%X", PtrToUint( hwnd ) );
hMap = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, hMap = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
0, sizeof(STATE), buf ); 0, sizeof(STATE), buf );
init = (GetLastError() != ERROR_ALREADY_EXISTS); init = (GetLastError() != ERROR_ALREADY_EXISTS);
@ -518,11 +523,11 @@ void get_state( void )
Info.dwSize = csbix.dwSize; Info.dwSize = csbix.dwSize;
ATTR = csbix.wAttributes; ATTR = csbix.wAttributes;
WIN = csbix.srWindow; WIN = csbix.srWindow;
memcpy( pState->o_palette, csbix.ColorTable, sizeof(csbix.ColorTable) ); arrcpy( pState->o_palette, csbix.ColorTable );
} }
else else
{ {
memcpy( pState->o_palette, legacy_palette, sizeof(legacy_palette) ); arrcpy( pState->o_palette, legacy_palette );
if (!GetConsoleScreenBufferInfo( hConOut, &Info )) if (!GetConsoleScreenBufferInfo( hConOut, &Info ))
{ {
DEBUGSTR( 1, "Failed to get screen buffer info (%u) - assuming defaults", DEBUGSTR( 1, "Failed to get screen buffer info (%u) - assuming defaults",
@ -536,35 +541,32 @@ void get_state( void )
BOTTOM = 24; BOTTOM = 24;
} }
} }
memcpy( pState->x_palette, xterm_palette, sizeof(xterm_palette) ); arrcpy( pState->x_palette, xterm_palette );
if (GetEnvironmentVariable( L"ANSICON_REVERSE", NULL, 0 ))
pState->sgr.foreground = attr2ansi[ATTR & 7];
pState->sgr.background = attr2ansi[(ATTR >> 4) & 7];
pState->sgr.bold = ATTR & FOREGROUND_INTENSITY;
pState->sgr.underline = ATTR & BACKGROUND_INTENSITY;
CloseHandle( hConOut );
}
if (!GetEnvironmentVariable( L"ANSICON_DEF", NULL, 0 ))
{
TCHAR def[4];
LPTSTR a = def;
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if (!GetConsoleScreenBufferInfo( hConOut, &Info ))
ATTR = 7;
if (pState->sgr.reverse)
{ {
SetEnvironmentVariable( L"ANSICON_REVERSE", NULL ); *a++ = '-';
pState->sgr.reverse = TRUE; ATTR = ((ATTR >> 4) & 15) | ((ATTR & 15) << 4);
pState->sgr.foreground = attr2ansi[(ATTR >> 4) & 7];
pState->sgr.background = attr2ansi[ATTR & 7];
pState->sgr.bold = (ATTR & BACKGROUND_INTENSITY) >> 4;
pState->sgr.underline = (ATTR & FOREGROUND_INTENSITY) << 4;
}
else
{
pState->sgr.foreground = attr2ansi[ATTR & 7];
pState->sgr.background = attr2ansi[(ATTR >> 4) & 7];
pState->sgr.bold = ATTR & FOREGROUND_INTENSITY;
pState->sgr.underline = ATTR & BACKGROUND_INTENSITY;
}
if (!GetEnvironmentVariable( L"ANSICON_DEF", NULL, 0 ))
{
TCHAR def[4];
LPTSTR a = def;
if (pState->sgr.reverse)
{
*a++ = '-';
ATTR = ((ATTR >> 4) & 15) | ((ATTR & 15) << 4);
}
_snwprintf( a, 3, L"%X", ATTR & 255 );
SetEnvironmentVariable( L"ANSICON_DEF", def );
} }
ac_wprintf( a, "%X", ATTR & 255 );
SetEnvironmentVariable( L"ANSICON_DEF", def );
set_ansicon( &Info ); set_ansicon( &Info );
CloseHandle( hConOut ); CloseHandle( hConOut );
} }
@ -611,7 +613,7 @@ BOOL search_env( LPCTSTR var, LPCTSTR val )
break; break;
} }
} while (*end != '\0'); } while (*end != '\0');
if (_wcsicmp( val, var ) == 0) if (lstrcmpi( val, var ) == 0)
return !not; return !not;
} }
@ -1002,7 +1004,7 @@ void SendSequence( LPTSTR seq )
DWORD len; DWORD len;
HANDLE hStdIn = GetStdHandle( STD_INPUT_HANDLE ); HANDLE hStdIn = GetStdHandle( STD_INPUT_HANDLE );
in = HeapAlloc( hHeap, HEAP_ZERO_MEMORY, 2 * wcslen( seq ) * sizeof(*in) ); in = HeapAlloc( hHeap, HEAP_ZERO_MEMORY, 2 * lstrlen( seq ) * sizeof(*in) );
if (in == NULL) if (in == NULL)
return; return;
for (len = 0; *seq; len += 2, ++seq) for (len = 0; *seq; len += 2, ++seq)
@ -1028,9 +1030,9 @@ void send_palette_sequence( COLORREF c )
g = GetGValue( c ); g = GetGValue( c );
b = GetBValue( c ); b = GetBValue( c );
if ((c & 0x0F0F0F) == ((c >> 4) & 0x0F0F0F)) if ((c & 0x0F0F0F) == ((c >> 4) & 0x0F0F0F))
_snwprintf( buf, lenof(buf), L"#%X%X%X", r & 0xF, g & 0xF, b & 0xF ); ac_wprintf( buf, "#%X%X%X", r & 0xF, g & 0xF, b & 0xF );
else else
_snwprintf( buf, lenof(buf), L"#%02X%02X%02X", r, g, b ); ac_wprintf( buf, "#%2X%2X%2X", r, g, b );
SendSequence( buf ); SendSequence( buf );
} }
@ -1040,7 +1042,7 @@ void init_tabs( int size )
{ {
int i; int i;
memset( pState->tab_stop, FALSE, MAX_TABS ); RtlZeroMemory( pState->tab_stop, MAX_TABS );
for (i = 0; i < MAX_TABS; i += size) for (i = 0; i < MAX_TABS; i += size)
pState->tab_stop[i] = TRUE; pState->tab_stop[i] = TRUE;
pState->tabs = TRUE; pState->tabs = TRUE;
@ -1130,12 +1132,12 @@ void Reset( BOOL hard )
csbix.cbSize = sizeof(csbix); csbix.cbSize = sizeof(csbix);
if (GetConsoleScreenBufferInfoX( hConOut, &csbix )) if (GetConsoleScreenBufferInfoX( hConOut, &csbix ))
{ {
memcpy( csbix.ColorTable, pState->o_palette, sizeof(csbix.ColorTable) ); arrcpy( csbix.ColorTable, pState->o_palette );
++csbix.srWindow.Right; ++csbix.srWindow.Right;
++csbix.srWindow.Bottom; ++csbix.srWindow.Bottom;
SetConsoleScreenBufferInfoX( hConOut, &csbix ); SetConsoleScreenBufferInfoX( hConOut, &csbix );
} }
memcpy( pState->x_palette, xterm_palette, sizeof(xterm_palette) ); arrcpy( pState->x_palette, xterm_palette );
} }
} }
@ -1370,7 +1372,9 @@ void InterpretEscSeq( void )
int a; int a;
*def = '7'; def[1] = '\0'; *def = '7'; def[1] = '\0';
GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) ); GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) );
a = wcstol( def, NULL, 16 ); a = ac_wcstol( def, NULL, 16 );
if (a == 0)
a = (*def == '-') ? -7 : 7;
pState->sgr.reverse = FALSE; pState->sgr.reverse = FALSE;
if (a < 0) if (a < 0)
{ {
@ -1696,7 +1700,7 @@ void InterpretEscSeq( void )
return; return;
case 3: // ESC[3g Clear all tabs case 3: // ESC[3g Clear all tabs
memset( pState->tab_stop, FALSE, MAX_TABS ); RtlZeroMemory( pState->tab_stop, MAX_TABS );
pState->tabs = TRUE; pState->tabs = TRUE;
return; return;
@ -1775,9 +1779,9 @@ void InterpretEscSeq( void )
case 6: // ESC[6n Report cursor position case 6: // ESC[6n Report cursor position
{ {
TCHAR buf[32]; TCHAR buf[32];
_snwprintf( buf, lenof(buf), L"\33[%d;%d%sR", ac_wprintf( buf, "\33[%d;%d%cR",
CUR.Y - top + 1, CUR.X + 1, CUR.Y - top + 1, CUR.X + 1,
(suffix2 == '+') ? L"+" : L"" ); (suffix2 == '+') ? '+' : '\0' );
SendSequence( buf ); SendSequence( buf );
} }
return; return;
@ -1904,7 +1908,7 @@ void InterpretEscSeq( void )
BOOL started = FALSE; BOOL started = FALSE;
for (beg = Pt_arg;; beg = end + 1) for (beg = Pt_arg;; beg = end + 1)
{ {
i = (int)wcstoul( beg, &end, 10 ); i = (int)ac_wcstoul( beg, &end, 10 );
if (end == beg || (*end != ';' && *end != '\0') || i >= 256) if (end == beg || (*end != ';' && *end != '\0') || i >= 256)
break; break;
if (end[2] == ';' || end[2] == '\0') if (end[2] == ';' || end[2] == '\0')
@ -1957,7 +1961,7 @@ void InterpretEscSeq( void )
if (*beg == '#') if (*beg == '#')
{ {
valid = TRUE; valid = TRUE;
c = (DWORD)wcstoul( ++beg, &end, 16 ); c = (DWORD)ac_wcstoul( ++beg, &end, 16 );
if (end - beg == 3) if (end - beg == 3)
{ {
r = (BYTE)(c >> 8); r = (BYTE)(c >> 8);
@ -1976,18 +1980,18 @@ void InterpretEscSeq( void )
else else
valid = FALSE; valid = FALSE;
} }
else if (wcsncmp( beg, L"rgb:", 4 ) == 0) else if (memcmp( beg, L"rgb:", 8 ) == 0)
{ {
valid = FALSE; valid = FALSE;
c = (DWORD)wcstoul( beg += 4, &end, 16 ); c = (DWORD)ac_wcstoul( beg += 4, &end, 16 );
if (*end == '/' && (end - beg == 2 || end - beg == 4)) if (*end == '/' && (end - beg == 2 || end - beg == 4))
{ {
r = (BYTE)(end - beg == 2 ? c : c >> 8); r = (BYTE)(end - beg == 2 ? c : c >> 8);
c = (DWORD)wcstoul( beg = end + 1, &end, 16 ); c = (DWORD)ac_wcstoul( beg = end + 1, &end, 16 );
if (*end == '/' && (end - beg == 2 || end - beg == 4)) if (*end == '/' && (end - beg == 2 || end - beg == 4))
{ {
g = (BYTE)(end - beg == 2 ? c : c >> 8); g = (BYTE)(end - beg == 2 ? c : c >> 8);
c = (DWORD)wcstoul( beg = end + 1, &end, 16 ); c = (DWORD)ac_wcstoul( beg = end + 1, &end, 16 );
if ((*end == ',' || *end == ';' || *end == '\0') && if ((*end == ',' || *end == ';' || *end == '\0') &&
(end - beg == 2 || end - beg == 4)) (end - beg == 2 || end - beg == 4))
{ {
@ -2000,15 +2004,15 @@ void InterpretEscSeq( void )
else else
{ {
valid = FALSE; valid = FALSE;
c = (DWORD)wcstoul( beg, &end, 10 ); c = (DWORD)ac_wcstoul( beg, &end, 10 );
if (*end == ',' && c < 256) if (*end == ',' && c < 256)
{ {
r = (BYTE)c; r = (BYTE)c;
c = (DWORD)wcstoul( end + 1, &end, 10 ); c = (DWORD)ac_wcstoul( end + 1, &end, 10 );
if (*end == ',' && c < 256) if (*end == ',' && c < 256)
{ {
g = (BYTE)c; g = (BYTE)c;
c = (DWORD)wcstoul( end + 1, &end, 10 ); c = (DWORD)ac_wcstoul( end + 1, &end, 10 );
if ((*end == ',' || *end == ';' || *end == '\0') && c < 256) if ((*end == ',' || *end == ';' || *end == '\0') && c < 256)
{ {
b = (BYTE)c; b = (BYTE)c;
@ -2043,15 +2047,15 @@ void InterpretEscSeq( void )
// Reset each index, or the entire palette. // Reset each index, or the entire palette.
if (Pt_len == 0) if (Pt_len == 0)
{ {
memcpy(csbix.ColorTable, pState->o_palette, sizeof(csbix.ColorTable)); arrcpy( csbix.ColorTable, pState->o_palette );
memcpy( pState->x_palette, xterm_palette, sizeof(xterm_palette) ); arrcpy( pState->x_palette, xterm_palette );
} }
else else
{ {
LPTSTR beg, end; LPTSTR beg, end;
for (beg = Pt_arg;; beg = end + 1) for (beg = Pt_arg;; beg = end + 1)
{ {
i = (int)wcstoul( beg, &end, 10 ); i = (int)ac_wcstoul( beg, &end, 10 );
if (end == beg || (*end != ';' && *end != '\0') || i >= 256) if (end == beg || (*end != ';' && *end != '\0') || i >= 256)
break; break;
if (i < 16) if (i < 16)
@ -2575,12 +2579,13 @@ ParseAndPrintString( HANDLE hDev,
// - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. // - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed.
const char APIKernel[] = "kernel32.dll"; const char APIKernel[] = "kernel32.dll";
const char APIConsole[] = "API-MS-Win-Core-Console-"; const char APIcore[] = "api-ms-win-core-";
const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-"; const char APIConsole[] = "console-";
const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-"; const char APIProcessThreads[] = "processthreads-";
const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-"; const char APIProcessEnvironment[] = "processenvironment-";
const char APIFile[] = "API-MS-Win-Core-File-"; const char APILibraryLoader[] = "libraryloader-";
const char APIHandle[] = "API-MS-Win-Core-Handle-"; const char APIFile[] = "file-";
const char APIHandle[] = "handle-";
typedef struct typedef struct
{ {
@ -2686,15 +2691,20 @@ BOOL HookAPIOneMod(
// for the module whose name matches the pszFunctionModule parameter. // for the module whose name matches the pszFunctionModule parameter.
for (; pImportDesc->Name; pImportDesc++) for (; pImportDesc->Name; pImportDesc++)
{ {
BOOL kernel = TRUE; BOOL kernel = EOF;
PSTR pszModName = MakeVA( PSTR, pImportDesc->Name ); PSTR pszModName = MakeVA( PSTR, pImportDesc->Name );
if (_strnicmp( pszModName, APIKernel, 8 ) != 0 || if (ac_strnicmp( pszModName, APIKernel, 8 ) == 0 &&
(_stricmp( pszModName+8, APIKernel+8 ) != 0 && pszModName[8] != '\0')) (pszModName[8] == '\0' ||
ac_strnicmp( pszModName+8, APIKernel+8, 5 ) == 0))
{
kernel = TRUE;
}
else if (ac_strnicmp( pszModName, APIcore, 16 ) == 0)
{ {
PAPI_DATA lib; PAPI_DATA lib;
for (lib = APIs; lib->name; ++lib) for (lib = APIs; lib->name; ++lib)
{ {
if (_strnicmp( pszModName, lib->name, lib->len ) == 0) if (ac_strnicmp( pszModName+16, lib->name, lib->len ) == 0)
{ {
if (lib->base == NULL) if (lib->base == NULL)
{ {
@ -2703,16 +2713,16 @@ BOOL HookAPIOneMod(
if (hook->lib == lib->name) if (hook->lib == lib->name)
hook->apifunc = GetProcAddress( lib->base, hook->name ); hook->apifunc = GetProcAddress( lib->base, hook->name );
} }
kernel = FALSE;
break; break;
} }
} }
if (lib->name == NULL) }
{ if (kernel == EOF)
if (log_level & 16) {
DEBUGSTR( 2, " %s%s %s", sp, zIgnoring, pszModName ); if (log_level & 16)
continue; DEBUGSTR( 2, " %s%s %s", sp, zIgnoring, pszModName );
} continue;
kernel = FALSE;
} }
if (log_level & 16) if (log_level & 16)
DEBUGSTR( 2, " %s%s %s", sp, zScanning, pszModName ); DEBUGSTR( 2, " %s%s %s", sp, zScanning, pszModName );
@ -2871,8 +2881,6 @@ BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore, BOOL indent )
static LPTSTR get_program( LPTSTR app, HANDLE hProcess, static LPTSTR get_program( LPTSTR app, HANDLE hProcess,
BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) BOOL wide, LPCVOID lpApp, LPCVOID lpCmd )
{ {
app[MAX_DEV_PATH-1] = '\0';
if (lpApp == NULL) if (lpApp == NULL)
{ {
typedef DWORD (WINAPI *PGPIFNW)( HANDLE, LPTSTR, DWORD ); typedef DWORD (WINAPI *PGPIFNW)( HANDLE, LPTSTR, DWORD );
@ -2905,7 +2913,7 @@ static LPTSTR get_program( LPTSTR app, HANDLE hProcess,
term = L"\""; term = L"\"";
++pos; ++pos;
} }
wcsncpy( app, pos, MAX_DEV_PATH-1 ); lstrcpyn( app, pos, MAX_DEV_PATH );
} }
else else
{ {
@ -2916,13 +2924,13 @@ static LPTSTR get_program( LPTSTR app, HANDLE hProcess,
term = L"\""; term = L"\"";
++pos; ++pos;
} }
MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_DEV_PATH-1 ); MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_DEV_PATH );
} }
// CreateProcess only works with surrounding quotes ('"a name"' works, // CreateProcess only works with surrounding quotes ('"a name"' works,
// but 'a" "name' fails), so that's all I'll test, too. However, it also // but 'a" "name' fails), so that's all I'll test, too. However, it also
// tests for a file at each separator ('a name' tries "a.exe" before // tests for a file at each separator ('a name' tries "a.exe" before
// "a name.exe") which I won't do. // "a name.exe") which I won't do.
name = wcspbrk( app, term ); name = ac_wcspbrk( app, term );
if (name != NULL) if (name != NULL)
*name = '\0'; *name = '\0';
} }
@ -2930,9 +2938,9 @@ static LPTSTR get_program( LPTSTR app, HANDLE hProcess,
else else
{ {
if (wide) if (wide)
wcsncpy( app, lpApp, MAX_DEV_PATH-1 ); lstrcpyn( app, lpApp, MAX_DEV_PATH );
else else
MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_DEV_PATH-1 ); MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_DEV_PATH );
} }
return get_program_name( app ); return get_program_name( app );
} }
@ -2994,8 +3002,8 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
TCHAR args[64]; TCHAR args[64];
STARTUPINFO si; STARTUPINFO si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
wcscpy( DllNameType, L"CON.exe" ); memcpy( DllNameType, L"CON.exe", 16 );
_snwprintf( args, lenof(args), L"ansicon -P%lu", child_pi->dwProcessId ); ac_wprintf( args, "ansicon -P%u", child_pi->dwProcessId );
ZeroMemory( &si, sizeof(si) ); ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si); si.cb = sizeof(si);
if (CreateProcess( DllName, args, NULL, NULL, FALSE, 0, NULL, NULL, if (CreateProcess( DllName, args, NULL, NULL, FALSE, 0, NULL, NULL,
@ -3007,7 +3015,7 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
} }
else else
DEBUGSTR( 1, "Could not execute %\"S (%u)", DllName, GetLastError() ); DEBUGSTR( 1, "Could not execute %\"S (%u)", DllName, GetLastError() );
wcscpy( DllNameType, L"32.dll" ); memcpy( DllNameType, L"32.dll", 14 );
} }
else else
#endif #endif
@ -3413,7 +3421,7 @@ WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer,
if (mb_size <= 4 && mb_size > len - pos) if (mb_size <= 4 && mb_size > len - pos)
{ {
mb_len = len - pos; mb_len = len - pos;
memcpy( mb, aBuf + pos, mb_len ); RtlMoveMemory( mb, aBuf + pos, mb_len );
len = pos; len = pos;
if (log_level & 4) if (log_level & 4)
{ {
@ -3569,10 +3577,13 @@ WINAPI MyCreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess,
{ {
if (dwDesiredAccess == GENERIC_WRITE) if (dwDesiredAccess == GENERIC_WRITE)
{ {
if (_stricmp( lpFileName, "con" ) == 0) PDWORD con = (PDWORD)lpFileName;
if ((con[0] | 0x202020) == 'noc' ||
((con[0] | 0x20202020) == 'onoc' && (con[1] | 0x2020) == '$tu'))
{
lpFileName = "CONOUT$"; lpFileName = "CONOUT$";
if (_stricmp( lpFileName, "CONOUT$" ) == 0)
dwDesiredAccess |= GENERIC_READ; dwDesiredAccess |= GENERIC_READ;
}
} }
return CreateFileA( lpFileName, dwDesiredAccess, dwShareMode, return CreateFileA( lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition, lpSecurityAttributes, dwCreationDisposition,
@ -3588,10 +3599,22 @@ WINAPI MyCreateFileW( LPCWSTR lpFileName, DWORD dwDesiredAccess,
{ {
if (dwDesiredAccess == GENERIC_WRITE) if (dwDesiredAccess == GENERIC_WRITE)
{ {
if (_wcsicmp( lpFileName, L"con" ) == 0) #ifdef _WIN64
__int64* con = (__int64*)lpFileName;
if ((con[0] | 0x2000200020) == 0x6E006F0063/*L'noc'*/ ||
((con[0] | 0x20002000200020) == 0x6F006E006F0063/*L'onoc'*/ &&
(con[1] | 0x200020) == 0x2400740075/*L'$tu'*/))
#else
PDWORD con = (PDWORD)lpFileName;
if ((con[0] | 0x200020) == 0x6F0063/*L'oc'*/ && ((con[1] | 0x20) == 'n' ||
((con[1] | 0x200020) == 0x6F006E/*L'on'*/ &&
(con[2] | 0x200020) == 0x740075/*L'tu'*/ &&
(con[3] == '$'))))
#endif
{
lpFileName = L"CONOUT$"; lpFileName = L"CONOUT$";
if (_wcsicmp( lpFileName, L"CONOUT$" ) == 0)
dwDesiredAccess |= GENERIC_READ; dwDesiredAccess |= GENERIC_READ;
}
} }
return CreateFileW( lpFileName, dwDesiredAccess, dwShareMode, return CreateFileW( lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition, lpSecurityAttributes, dwCreationDisposition,
@ -3708,7 +3731,7 @@ void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi )
pcsbi = &csbi; pcsbi = &csbi;
} }
_snwprintf( buf, lenof(buf), L"%dx%d (%dx%d)", ac_wprintf( buf, "%dx%d (%dx%d)",
pcsbi->dwSize.X, pcsbi->dwSize.Y, pcsbi->dwSize.X, pcsbi->dwSize.Y,
pcsbi->srWindow.Right - pcsbi->srWindow.Left + 1, pcsbi->srWindow.Right - pcsbi->srWindow.Left + 1,
pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1 ); pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1 );
@ -3718,7 +3741,7 @@ void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi )
DWORD DWORD
WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize )
{ {
if (_stricmp( lpName, "ANSICON_VER" ) == 0) if (lstrcmpiA( lpName, "ANSICON_VER" ) == 0)
{ {
if (nSize < sizeof(PVEREA)) if (nSize < sizeof(PVEREA))
return sizeof(PVEREA); return sizeof(PVEREA);
@ -3726,7 +3749,7 @@ WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize )
return sizeof(PVEREA) - 1; return sizeof(PVEREA) - 1;
} }
if (_stricmp( lpName, "CLICOLOR" ) == 0) if (lstrcmpiA( lpName, "CLICOLOR" ) == 0)
{ {
if (nSize < 2) if (nSize < 2)
return 2; return 2;
@ -3735,7 +3758,7 @@ WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize )
return 1; return 1;
} }
if (_stricmp( lpName, "ANSICON" ) == 0) if (lstrcmpiA( lpName, "ANSICON" ) == 0)
set_ansicon( NULL ); set_ansicon( NULL );
return GetEnvironmentVariableA( lpName, lpBuffer, nSize ); return GetEnvironmentVariableA( lpName, lpBuffer, nSize );
@ -3744,7 +3767,7 @@ WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize )
DWORD DWORD
WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize ) WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize )
{ {
if (_wcsicmp( lpName, L"ANSICON_VER" ) == 0) if (lstrcmpi( lpName, L"ANSICON_VER" ) == 0)
{ {
if (nSize < lenof(PVERE)) if (nSize < lenof(PVERE))
return lenof(PVERE); return lenof(PVERE);
@ -3752,7 +3775,7 @@ WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize )
return lenof(PVERE) - 1; return lenof(PVERE) - 1;
} }
if (_wcsicmp( lpName, L"CLICOLOR" ) == 0) if (lstrcmpi( lpName, L"CLICOLOR" ) == 0)
{ {
if (nSize < 2) if (nSize < 2)
return 2; return 2;
@ -3761,7 +3784,7 @@ WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize )
return 1; return 1;
} }
if (_wcsicmp( lpName, L"ANSICON" ) == 0) if (lstrcmpi( lpName, L"ANSICON" ) == 0)
set_ansicon( NULL ); set_ansicon( NULL );
return GetEnvironmentVariableW( lpName, lpBuffer, nSize ); return GetEnvironmentVariableW( lpName, lpBuffer, nSize );
@ -3832,12 +3855,14 @@ void OriginalAttr( PVOID lpReserved )
{ {
HANDLE hConOut; HANDLE hConOut;
CONSOLE_SCREEN_BUFFER_INFO Info; CONSOLE_SCREEN_BUFFER_INFO Info;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNTHeader;
BOOL org;
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, get_state();
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL ); pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( NULL );
if (!GetConsoleScreenBufferInfo( hConOut, &Info )) pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew );
ATTR = 7;
// If we were loaded dynamically, remember the current attributes to restore // If we were loaded dynamically, remember the current attributes to restore
// upon unloading. However, if we're the 64-bit DLL, but the image is 32- // upon unloading. However, if we're the 64-bit DLL, but the image is 32-
@ -3845,27 +3870,35 @@ void OriginalAttr( PVOID lpReserved )
// be dynamic due to lack of the IAT. // be dynamic due to lack of the IAT.
if (lpReserved == NULL) if (lpReserved == NULL)
{ {
BOOL dynamic = TRUE; org = TRUE;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNTHeader;
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( NULL );
pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew );
#ifdef _WIN64 #ifdef _WIN64
if (pNTHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) if (pNTHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
dynamic = FALSE; org = FALSE;
else else
#endif #endif
if (pNTHeader->DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT && if (pNTHeader->DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT &&
get_os_version() >= 0x602) get_os_version() >= 0x602)
dynamic = FALSE; org = FALSE;
if (dynamic) }
orgattr = ATTR; else
{
// We also want to restore the original attributes for ansicon.exe.
org = (pNTHeader->OptionalHeader.MajorImageVersion == 20033 && // 'AN'
pNTHeader->OptionalHeader.MinorImageVersion == 18771); // 'SI'
}
if (org)
{
hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if (!GetConsoleScreenBufferInfo( hConOut, &Info ))
ATTR = 7;
orgattr = ATTR;
orgsgr = pState->sgr;
GetConsoleMode( hConOut, &orgmode ); GetConsoleMode( hConOut, &orgmode );
GetConsoleCursorInfo( hConOut, &orgcci ); GetConsoleCursorInfo( hConOut, &orgcci );
CloseHandle( hConOut );
} }
CloseHandle( hConOut );
get_state();
} }
@ -3888,8 +3921,6 @@ DWORD WINAPI exit_thread( LPVOID lpParameter )
// and terminated. // and terminated.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Need to export something for static loading to work, this is as good as any.
__declspec(dllexport)
BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
{ {
BOOL bResult = TRUE; BOOL bResult = TRUE;
@ -3901,7 +3932,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
if (dwReason == DLL_PROCESS_ATTACH) if (dwReason == DLL_PROCESS_ATTACH)
{ {
hHeap = HeapCreate( 0, 0, 128 * 1024 ); hHeap = HeapCreate( 0, 0, 256 * 1024 );
hKernel = GetModuleHandleA( APIKernel ); hKernel = GetModuleHandleA( APIKernel );
GetConsoleScreenBufferInfoX = (PHCSBIX)GetProcAddress( GetConsoleScreenBufferInfoX = (PHCSBIX)GetProcAddress(
hKernel, "GetConsoleScreenBufferInfoEx" ); hKernel, "GetConsoleScreenBufferInfoEx" );
@ -3914,7 +3945,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
*logstr = '\0'; *logstr = '\0';
GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) );
log_level = _wtoi( logstr ); log_level = ac_wtoi( logstr );
prog = get_program_name( NULL ); prog = get_program_name( NULL );
#if defined(_WIN64) || defined(W32ON64) #if defined(_WIN64) || defined(W32ON64)
DllNameType = DllName - 6 + DllNameType = DllName - 6 +
@ -3982,6 +4013,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
SetConsoleMode( hConOut, orgmode ); SetConsoleMode( hConOut, orgmode );
SetConsoleCursorInfo( hConOut, &orgcci ); SetConsoleCursorInfo( hConOut, &orgcci );
CloseHandle( hConOut ); CloseHandle( hConOut );
pState->sgr = orgsgr;
} }
if (hMap != NULL) if (hMap != NULL)
{ {

150
ansicon.c
View File

@ -89,9 +89,12 @@
v1.80, 28 October & 30 November, 2017: v1.80, 28 October & 30 November, 2017:
write newline with _putws, not putwchar (fixes redirecting to CON); write newline with _putws, not putwchar (fixes redirecting to CON);
use -pu to unload from the parent. use -pu to unload from the parent.
v1.84, 7 May, 2018:
import the DLL.
*/ */
#define PDATE L"4 May, 2018" #define PDATE L"7 May, 2018"
#include "ansicon.h" #include "ansicon.h"
#include "version.h" #include "version.h"
@ -128,25 +131,6 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR );
static HANDLE hConOut; 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 // The fputws function in MSVCRT.DLL (Windows 7 x64) is broken for Unicode
@ -172,53 +156,6 @@ int my_fputws( const wchar_t* s, FILE* f )
#define _putws( s ) my_fputws( s L"\n", stdout ) #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
_snwprintf( DllName + len, MAX_PATH-1 - 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)
RemoteLoad64( ppi );
#else
wcscpy( DllName + len, L"ANSI32.dll" );
set_ansi_dll();
InjectDLL( ppi, base );
#endif
return TRUE;
}
// Use CreateRemoteThread to (un)load our DLL in the target process. // Use CreateRemoteThread to (un)load our DLL in the target process.
void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
{ {
@ -227,7 +164,6 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
PBYTE proc; PBYTE proc;
DWORD rva; DWORD rva;
BOOL fOk; BOOL fOk;
DWORD len;
LPVOID param; LPVOID param;
HANDLE thread; HANDLE thread;
DWORD ticks; DWORD ticks;
@ -236,7 +172,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
int type; int type;
#endif #endif
DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId ); DEBUGSTR( 1, "Parent = %S (%u)", app, ppi->dwProcessId );
// Find the base address of kernel32.dll. // Find the base address of kernel32.dll.
ticks = GetTickCount(); ticks = GetTickCount();
@ -266,11 +202,13 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
return; return;
} }
proc = param = NULL; proc = param = NULL;
len = (DWORD)(prog - prog_path);
memcpy( DllName, prog_path, TSIZE(len) );
#ifdef _WIN64 #ifdef _WIN64
type = (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) ? 32 : 64; type = 64;
_snwprintf( DllName + len, MAX_PATH-1 - len, L"ANSI%d.dll", type ); if (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64)
{
type = 32;
*(PDWORD)DllNameType = 0x320033/*L'23'*/;
}
#endif #endif
me.dwSize = sizeof(MODULEENTRY32); me.dwSize = sizeof(MODULEENTRY32);
for (fOk = Module32First( hSnap, &me ); fOk; fOk = Module32Next( hSnap, &me )) for (fOk = Module32First( hSnap, &me ); fOk; fOk = Module32Next( hSnap, &me ))
@ -278,17 +216,21 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0) if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0)
{ {
proc = me.modBaseAddr; proc = me.modBaseAddr;
if (!unload) if (!unload || param)
break; break;
} }
else if (unload) else if (unload)
{ {
#ifdef _WIN64 #ifdef _WIN64
if (_wcsicmp( me.szModule, DllName + len ) == 0) if (_wcsicmp( me.szModule, DllNameType - 4 ) == 0)
#else #else
if (_wcsicmp( me.szModule, L"ANSI32.dll" ) == 0) if (_wcsicmp( me.szModule, L"ANSI32.dll" ) == 0)
#endif #endif
{
param = me.modBaseAddr; param = me.modBaseAddr;
if (proc)
break;
}
} }
} }
CloseHandle( hSnap ); CloseHandle( hSnap );
@ -307,7 +249,6 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
rva = GetProcRVA( L"kernel32.dll", (unload) ? "FreeLibrary" rva = GetProcRVA( L"kernel32.dll", (unload) ? "FreeLibrary"
: "LoadLibraryW", type ); : "LoadLibraryW", type );
#else #else
wcscpy( DllName + len, L"ANSI32.dll" );
rva = GetProcRVA( L"kernel32.dll", unload ? "FreeLibrary" : "LoadLibraryW" ); rva = GetProcRVA( L"kernel32.dll", unload ? "FreeLibrary" : "LoadLibraryW" );
#endif #endif
if (rva == 0) if (rva == 0)
@ -316,13 +257,14 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload )
if (!unload) if (!unload)
{ {
DWORD len = TSIZE((DWORD)wcslen( DllName ) + 1);
param = VirtualAllocEx(ppi->hProcess, NULL, len, MEM_COMMIT,PAGE_READWRITE); param = VirtualAllocEx(ppi->hProcess, NULL, len, MEM_COMMIT,PAGE_READWRITE);
if (param == NULL) if (param == NULL)
{ {
DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError()); DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError());
goto no_go; goto no_go;
} }
WriteProcMem( param, DllName, TSIZE(len + 11) ); WriteProcMem( param, DllName, len );
} }
thread = CreateRemoteThread( ppi->hProcess, NULL, 4096, thread = CreateRemoteThread( ppi->hProcess, NULL, 4096,
(LPTHREAD_START_ROUTINE)proc, param, 0, NULL ); (LPTHREAD_START_ROUTINE)proc, param, 0, NULL );
@ -346,7 +288,6 @@ int main( void )
LPTSTR argv, arg, cmd; LPTSTR argv, arg, cmd;
TCHAR buf[4]; TCHAR buf[4];
BOOL shell, run, gui; BOOL shell, run, gui;
HMODULE ansi;
DWORD len; DWORD len;
int rc = 0; int rc = 0;
@ -361,7 +302,9 @@ int main( void )
_setmode( 2, _O_U16TEXT); _setmode( 2, _O_U16TEXT);
// Create a console handle and store the current attributes. // Create a console handle and store the current attributes.
get_original_attr(); hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0 );
argv = GetCommandLine(); argv = GetCommandLine();
len = (DWORD)wcslen( argv ) + 1; len = (DWORD)wcslen( argv ) + 1;
@ -386,9 +329,6 @@ int main( void )
} }
} }
hHeap = HeapCreate( 0, 0, 65 * 1024 );
prog = get_program_name( NULL );
*buf = '\0'; *buf = '\0';
GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) ); GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) );
log_level = _wtoi( buf ); log_level = _wtoi( buf );
@ -405,7 +345,12 @@ int main( void )
} }
else else
{ {
Inject( &pi, &gui, NULL ); PBYTE base;
DEBUGSTR( 1, "64-bit process (%u) started by 32-bit", pi.dwProcessId );
if (ProcessType( &pi, &base, NULL ) == 48)
RemoteLoad64( &pi );
else
InjectDLL( &pi, base );
CloseHandle( pi.hProcess ); CloseHandle( pi.hProcess );
} }
return 0; return 0;
@ -456,20 +401,8 @@ int main( void )
} }
case 'm': case 'm':
{ SetEnvironmentVariable( L"ANSICON_DEF", arg[2] ? arg + 2 : L"7" );
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; break;
}
case 'e': case 'e':
case 'E': case 'E':
@ -494,13 +427,7 @@ arg_out:
} }
// Ensure the default attributes are the current attributes. // Ensure the default attributes are the current attributes.
if (GetEnvironmentVariable( L"ANSICON_DEF", buf, lenof(buf) ) != 0) WriteConsole( hConOut, L"\33[m", 3, &len, NULL );
{
int a = wcstol( buf, NULL, 16 );
if (a < 0)
a = ((-a >> 4) & 15) | ((-a & 15) << 4);
SetConsoleTextAttribute( hConOut, (WORD)a );
}
if (run) if (run)
{ {
@ -518,11 +445,9 @@ arg_out:
ZeroMemory( &si, sizeof(si) ); ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si); si.cb = sizeof(si);
if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ))
NULL, NULL, &si, &pi ))
{ {
Inject( &pi, &gui, arg ); ProcessType( &pi, NULL, &gui );
ResumeThread( pi.hThread );
if (!gui) if (!gui)
{ {
SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE );
@ -540,13 +465,7 @@ arg_out:
} }
else if (*arg) else if (*arg)
{ {
ansi = LoadLibrary( ANSIDLL ); if (*arg == 'e' || *arg == 'E')
if (ansi == NULL)
{
print_error( ANSIDLL );
rc = 1;
}
else if (*arg == 'e' || *arg == 'E')
{ {
cmd += 2; cmd += 2;
if (*cmd == ' ' || *cmd == '\t') if (*cmd == ' ' || *cmd == '\t')
@ -574,11 +493,8 @@ arg_out:
get_file( arg, &argv, &cmd ); get_file( arg, &argv, &cmd );
} while (*arg); } while (*arg);
} }
FreeLibrary( ansi );
} }
set_original_attr();
return rc; return rc;
} }

View File

@ -83,34 +83,62 @@ typedef struct IMAGE_COR20_HEADER
#define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr ) #define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr )
int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* ); #ifdef PDATE // i.e. from ansicon.c
#define EXTERN __declspec(dllimport) extern
#else
#define EXTERN __declspec(dllexport) extern
#endif
EXTERN int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* );
BOOL Wow64Process( HANDLE ); BOOL Wow64Process( HANDLE );
#ifdef _WIN64
EXTERN
#endif
void InjectDLL( LPPROCESS_INFORMATION, PBYTE ); void InjectDLL( LPPROCESS_INFORMATION, PBYTE );
void RemoteLoad32( LPPROCESS_INFORMATION ); void RemoteLoad32( LPPROCESS_INFORMATION );
#ifdef _WIN64 #ifdef _WIN64
void InjectDLL32( LPPROCESS_INFORMATION, PBYTE ); void InjectDLL32( LPPROCESS_INFORMATION, PBYTE );
void RemoteLoad64( LPPROCESS_INFORMATION ); EXTERN void RemoteLoad64( LPPROCESS_INFORMATION );
DWORD GetProcRVA( LPCTSTR, LPCSTR, int ); EXTERN DWORD GetProcRVA( LPCTSTR, LPCSTR, int );
#else #else
DWORD GetProcRVA( LPCTSTR, LPCSTR ); EXTERN DWORD GetProcRVA( LPCTSTR, LPCSTR );
#endif #endif
extern HANDLE hHeap; extern HANDLE hHeap;
extern TCHAR prog_path[MAX_PATH]; EXTERN TCHAR prog_path[MAX_PATH];
extern LPTSTR prog; extern LPTSTR prog;
LPTSTR get_program_name( LPTSTR ); LPTSTR get_program_name( LPTSTR );
extern TCHAR DllName[MAX_PATH]; EXTERN TCHAR DllName[MAX_PATH];
extern LPTSTR DllNameType; EXTERN LPTSTR DllNameType;
extern char ansi_dll[MAX_PATH]; extern char ansi_dll[MAX_PATH];
extern DWORD ansi_len; extern DWORD ansi_len;
extern char* ansi_bits; extern char* ansi_bits;
void set_ansi_dll( void ); void set_ansi_dll( void );
DWORD get_os_version( void ); DWORD get_os_version( void );
extern int log_level; EXTERN int log_level;
void DEBUGSTR( int level, LPCSTR szFormat, ... ); EXTERN void DEBUGSTR( int level, LPCSTR szFormat, ... );
// Replacements for C runtime functions.
#undef RtlFillMemory
#undef RtlMoveMemory
#undef RtlZeroMemory
void WINAPI RtlFillMemory( PVOID, SIZE_T, BYTE );
void WINAPI RtlMoveMemory( PVOID, const VOID*, SIZE_T );
void WINAPI RtlZeroMemory( PVOID, SIZE_T );
#define arrcpy( dst, src ) RtlMoveMemory( dst, src, sizeof(dst) )
unsigned long ac_wcstoul( const wchar_t*, wchar_t**, int );
int ac_wtoi( const wchar_t* );
long ac_wcstol( const wchar_t*, wchar_t**, int );
wchar_t* ac_wcspbrk( const wchar_t*, const wchar_t* );
wchar_t* ac_wcsrchr( const wchar_t*, wchar_t );
int ac_strnicmp( const char*, const char*, size_t );
int ac_sprintf( char*, const char*, ... );
int ac_wprintf( wchar_t*, const char*, ... );
#endif #endif

View File

@ -117,7 +117,7 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
ip.pL = (PLONG_PTR)pImports; ip.pL = (PLONG_PTR)pImports;
*ip.pL++ = IMAGE_ORDINAL_FLAG + 1; *ip.pL++ = IMAGE_ORDINAL_FLAG + 1;
*ip.pL++ = 0; *ip.pL++ = 0;
memcpy( ip.pB, ansi_dll, ansi_len ); RtlMoveMemory( ip.pB, ansi_dll, ansi_len );
ip.pB += ansi_len; ip.pB += ansi_len;
ip.pI->OriginalFirstThunk = 0; ip.pI->OriginalFirstThunk = 0;
ip.pI->TimeDateStamp = 0; ip.pI->TimeDateStamp = 0;
@ -213,7 +213,7 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
ip.pL = (PLONG)pImports; ip.pL = (PLONG)pImports;
*ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1; *ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1;
*ip.pL++ = 0; *ip.pL++ = 0;
memcpy( ip.pB, ansi_dll, ansi_len ); RtlMoveMemory( ip.pB, ansi_dll, ansi_len );
ip.pB += ansi_len; ip.pB += ansi_len;
ip.pI->OriginalFirstThunk = 0; ip.pI->OriginalFirstThunk = 0;
ip.pI->TimeDateStamp = 0; ip.pI->TimeDateStamp = 0;
@ -331,7 +331,7 @@ void RemoteLoad64( LPPROCESS_INFORMATION ppi )
return; return;
} }
len = (DWORD)TSIZE(wcslen( DllName ) + 1); len = (DWORD)TSIZE(lstrlen( DllName ) + 1);
ip.pB = code; ip.pB = code;
*ip.pL++ = ntdll + rLdrLoadDll; // address of LdrLoadDll *ip.pL++ = ntdll + rLdrLoadDll; // address of LdrLoadDll
@ -400,7 +400,7 @@ void RemoteLoad32( LPPROCESS_INFORMATION ppi )
} }
bMem = PtrToUint( pMem ); bMem = PtrToUint( pMem );
len = (DWORD)TSIZE(wcslen( DllName ) + 1); len = (DWORD)TSIZE(lstrlen( DllName ) + 1);
ip.pB = code; ip.pB = code;
*ip.pS++ = 0x5451; // push ecx esp *ip.pS++ = 0x5451; // push ecx esp

View File

@ -22,6 +22,9 @@
# 30 April, 2018: # 30 April, 2018:
# use undocumented rc option /s to remove its logo; # use undocumented rc option /s to remove its logo;
# use a batch rule (even if this project is too small to make a difference). # use a batch rule (even if this project is too small to make a difference).
#
# 8 May, 2018:
# rc /s only removes the logo as a side-effect; use /nologo when available.
#BITS = 32 #BITS = 32
#BITS = 64 #BITS = 64
@ -39,20 +42,26 @@ DIR = x86
!ELSE !ELSE
!IF $(BITS) == 64 !IF $(BITS) == 64
DIR = x64 DIR = x64
RFLAGS = /D_WIN64
!ELSE !ELSE
!ERROR BITS should be defined to 32 or 64. !ERROR BITS should be defined to 32 or 64.
!ENDIF !ENDIF
!ENDIF !ENDIF
# This is required for the 2003 Platform SDK, but not for Visual Studio 2010. # Disable security checks, but VC6 & 7 don't have /GS-.
!IF "$(_NMAKE_VER)" == "7.00.8882" !IF "$(_NMAKE_VER)" == "7.00.8882" && $(BITS) == 32
!IF $(BITS) == 64 NOSECCHK =
LIBS64 = bufferoverflowu.lib RFLAGS =
# The 2003 Toolkit doesn't have MSVCRT.LIB, but VC98 does. # The 2003 Toolkit doesn't have MSVCRT.LIB, but VC98 does.
!ELSEIF !DEFINED(SHARE) && !DEFINED(MSVCDIR) !IF !DEFINED(SHARE) && !DEFINED(MSVCDIR)
SHARE = SHARE =
!ENDIF !ENDIF
!ELSE
NOSECCHK = /GS-
!ENDIF
# 2008 (SDK v6) and earlier rc do not have /nologo.
!IF [cmd /d /c exit /b $(_NMAKE_VER)] <= 9
RFLAGS =
!ENDIF !ENDIF
# Link with MSVCRT.LIB by default. # Link with MSVCRT.LIB by default.
@ -63,9 +72,14 @@ SHARE = /MD
# Manifest tool to embed the manifest required by 2008. # Manifest tool to embed the manifest required by 2008.
MT = mt.exe MT = mt.exe
RFLAGS = /s !IFNDEF RFLAGS
CFLAGS = /nologo /W3 /O2 $(SHARE) /D_CRT_SECURE_NO_WARNINGS RFLAGS = /nologo
LIBS = advapi32.lib $(LIBS64) !ENDIF
!IF $(BITS) == 64
RFLAGS = $(RFLAGS) /D_WIN64
!ENDIF
CFLAGS = /nologo /W3 /O2 $(NOSECCHK) /D_CRT_SECURE_NO_WARNINGS
LIBS = kernel32.lib advapi32.lib
# Identify ansicon.exe using "ANSI" as a version number. # Identify ansicon.exe using "ANSI" as a version number.
LINK = /link /version:20033.18771 LINK = /link /version:20033.18771
@ -92,14 +106,14 @@ MTmsg = @echo Embedding manifest&
all: ansicon$(BITS) all: ansicon$(BITS)
ansicon32: x86 x86\ansicon.exe x86\ANSI32.dll x64 x64\ANSI32.dll ansicon32: x86 x86\ANSI32.dll x86\ansicon.exe x64 x64\ANSI32.dll
ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll ansicon64: x64 x64\ANSI64.dll x64\ansicon.exe
x86: x86:
mkdir x86 mkdir x86
x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res x86\ansicon.exe: x86\ansicon.obj x86\ansi32.lib x86\ansicon.res
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512 $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512
!IF "$(_NMAKE_VER)" == "9.00.30729.01" !IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1 $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1
@ -107,30 +121,23 @@ x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res
!ENDIF !ENDIF
x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ $(LDmsg)$(CC) /nologo /LD /Fe$@ $** $(LIBS) /link \
/base:0xAC0000 /filealign:512 /base:0xAC0000 /entry:DllMain /filealign:512
!IF "$(_NMAKE_VER)" == "9.00.30729.01"
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
@del $@.manifest
!ENDIF
x64: x64:
mkdir x64 mkdir x64
x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res x64\ansicon.exe: x64\ansicon.obj x64\ansi64.lib x64\ansicon.res
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK)
x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ $(LDmsg)$(CC) /nologo /LD /Fe$@ $** $(LIBS) /link \
/base:0xAC000000 /base:0xAC000000 /entry:DllMain
x64\ANSI32.dll: x64\ANSI32.obj $(X6432OBJS) x86\ansi.res x64\ANSI32.dll: x64\ANSI32.obj $(X6432OBJS) x86\ansi.res
$(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ $(LDmsg)$(CC) /nologo /LD /Fe$@ $** $(LIBS) /link \
/base:0xAC0000 /filealign:512 /largeaddressaware /base:0xAC0000 /entry:DllMain /filealign:512 \
!IF "$(_NMAKE_VER)" == "9.00.30729.01" /largeaddressaware
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2
@del $@.manifest
!ENDIF
ansicon.c: ansicon.h version.h ansicon.c: ansicon.h version.h
ansicon.rc: version.h ansicon.rc: version.h
@ -141,6 +148,9 @@ injdll.c: ansicon.h
proctype.c: ansicon.h proctype.c: ansicon.h
procrva.c: ansicon.h procrva.c: ansicon.h
$(DIR)\ansicon.obj:
$(CCmsg)$(CC) /c $(CFLAGS) $(SHARE) /Fo$@ $?
x64\ANSI32.obj: ANSI.c x64\ANSI32.obj: ANSI.c
$(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?

View File

@ -32,7 +32,7 @@ DWORD GetProcRVA( LPCTSTR module, LPCSTR func )
#endif #endif
len = GetSystemDirectory( buf, MAX_PATH ); len = GetSystemDirectory( buf, MAX_PATH );
buf[len++] = '\\'; buf[len++] = '\\';
wcscpy( buf + len, module ); lstrcpy( buf + len, module );
hMod = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE ); hMod = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE );
if (hMod == NULL) if (hMod == NULL)
{ {

View File

@ -46,6 +46,22 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
MEMORY_BASIC_INFORMATION minfo; MEMORY_BASIC_INFORMATION minfo;
IMAGE_DOS_HEADER dos_header; IMAGE_DOS_HEADER dos_header;
IMAGE_NT_HEADERS nt_header; IMAGE_NT_HEADERS nt_header;
PBYTE dummy_base;
BOOL dummy_gui;
BOOL skip_log;
// There's no need to log if we're only getting one value.
skip_log = FALSE;
if (pBase == NULL)
{
pBase = &dummy_base;
skip_log = TRUE;
}
if (gui == NULL)
{
gui = &dummy_gui;
skip_log = TRUE;
}
*pBase = NULL; *pBase = NULL;
*gui = FALSE; *gui = FALSE;
@ -62,7 +78,7 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
&& nt_header.Signature == IMAGE_NT_SIGNATURE && nt_header.Signature == IMAGE_NT_SIGNATURE
&& !(nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL)) && !(nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL))
{ {
// Don't load into ansicon.exe, it wants to do that itself. // Don't load into ansicon.exe, it's already imported.
if (nt_header.OptionalHeader.MajorImageVersion == 20033 && // 'AN' if (nt_header.OptionalHeader.MajorImageVersion == 20033 && // 'AN'
nt_header.OptionalHeader.MinorImageVersion == 18771) // 'SI' nt_header.OptionalHeader.MinorImageVersion == 18771) // 'SI'
return -1; return -1;
@ -102,27 +118,27 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
#endif #endif
} }
} }
DEBUGSTR( 1, " 32-bit %s (base = %q)", if (!skip_log)
(*gui) ? "GUI" : "console", minfo.BaseAddress ); DEBUGSTR( 1, " 32-bit %s (base = %q)",
(*gui) ? "GUI" : "console", minfo.BaseAddress );
return 32; return 32;
} }
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
{ {
#ifdef _WIN64 #ifdef _WIN64
DEBUGSTR( 1, " 64-bit %s (base = %p)", if (!skip_log)
(*gui) ? "GUI" : "console", minfo.BaseAddress ); DEBUGSTR( 1, " 64-bit %s (base = %p)",
return 64; (*gui) ? "GUI" : "console", minfo.BaseAddress );
#elif defined(W32ON64)
// Console will log due to -P, but GUI may be ignored (if not,
// this'll show up twice).
if (*gui)
DEBUGSTR( 1, " 64-bit GUI (base = %P)", minfo.BaseAddress );
return 64; return 64;
#else #else
DEBUGSTR( 1, " 64-bit %s (base = %P)", DEBUGSTR( 1, " 64-bit %s (base = %P)",
(*gui) ? "GUI" : "console", minfo.BaseAddress ); (*gui) ? "GUI" : "console", minfo.BaseAddress );
#if defined(W32ON64)
return 64;
#else
DEBUGSTR( 1, " Unsupported (use x64\\ansicon)" ); DEBUGSTR( 1, " Unsupported (use x64\\ansicon)" );
return 0; return 0;
#endif
#endif #endif
} }
DEBUGSTR( 1, " Ignoring unsupported machine (0x%X)", DEBUGSTR( 1, " Ignoring unsupported machine (0x%X)",

View File

@ -339,13 +339,16 @@ Version History
Legend: + added, - bug-fixed, * changed. Legend: + added, - bug-fixed, * changed.
1.84-wip - 4 May, 2018: 1.84-wip - 7 May, 2018:
- close the flush handles on detach; - close the flush handles on detach;
- WriteFile wasn't properly testing if its handle was for a console; - WriteFile wasn't properly testing if its handle was for a console;
- use remote load on Win8+ if the process has no IAT; - use remote load on Win8+ if the process has no IAT;
* remove dependency on USER32, dynamically load WINMM; - fix logging really long command lines;
- default to 7 or -7 if ANSICON_DEF could not be parsed;
* remove dependency on CRT & USER32, dynamically load WINMM;
* exit process if the primary thread is detached (for processes on Win10 * exit process if the primary thread is detached (for processes on Win10
that return, rather than call ExitProcess). that return, rather than call ExitProcess);
* ansicon.exe statically loads the DLL.
1.83 - 16 February, 2018: 1.83 - 16 February, 2018:
- create the flush thread on first use. - create the flush thread on first use.
@ -621,4 +624,4 @@ Distribution
======================== ========================
Jason Hood, 4 May, 2018. Jason Hood, 7 May, 2018.

308
util.c
View File

@ -30,12 +30,12 @@ LPTSTR get_program_name( LPTSTR program )
GetModuleFileName( NULL, prog_path, lenof(prog_path) ); GetModuleFileName( NULL, prog_path, lenof(prog_path) );
program = prog_path; program = prog_path;
} }
name = wcsrchr( program, '\\' ); name = ac_wcsrchr( program, '\\' );
if (name != NULL) if (name != NULL)
++name; ++name;
else else
name = program; name = program;
ext = wcsrchr( name, '.' ); ext = ac_wcsrchr( name, '.' );
if (ext != NULL && ext != name) if (ext != NULL && ext != name)
*ext = '\0'; *ext = '\0';
@ -107,13 +107,13 @@ static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len )
src.a = (LPSTR)str; src.a = (LPSTR)str;
if (len == 0 && str != 0) if (len == 0 && str != 0)
len = (DWORD)(wide ? wcslen( src.w ) : strlen( src.a )); len = (DWORD)(wide ? lstrlen( src.w ) : strlen( src.a ));
if (pos + len * 6 + 8 >= buf_len) if (pos + len * 6 + 8 >= buf_len)
{ {
LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + len * 6 + 8 ); LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + len * 6 + 8 );
if (tmp == NULL) if (tmp == NULL)
return 0; return pos;
buf = tmp; buf = tmp;
buf_len = (DWORD)HeapSize( hHeap, 0, buf ); buf_len = (DWORD)HeapSize( hHeap, 0, buf );
} }
@ -185,7 +185,7 @@ static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len )
case '\r': buf[pos++] = 'r'; break; case '\r': buf[pos++] = 'r'; break;
case 27 : buf[pos++] = 'e'; break; case 27 : buf[pos++] = 'e'; break;
default: default:
pos += sprintf( buf + pos, "x%.2X", ch ); pos += ac_sprintf( buf + pos, "x%2X", ch );
} }
} }
else else
@ -214,7 +214,7 @@ static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len )
} }
} }
if (quote && start_trail) if (quote && start_trail)
pos += sprintf( buf + pos, "\\x%.2X", ch ); pos += ac_sprintf( buf + pos, "\\x%2X", ch );
else else
buf[pos++] = ch; buf[pos++] = ch;
} }
@ -223,8 +223,10 @@ static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len )
int mb = WideCharToMultiByte( cp, flags, src.w - 1, 1, buf + pos, 12, int mb = WideCharToMultiByte( cp, flags, src.w - 1, 1, buf + pos, 12,
NULL, pDef ); NULL, pDef );
if (def) if (def)
mb = sprintf( buf + pos, ch < 0x100 ? "%cx%.2X" : "%cu%.4X", {
(quote) ? '\\' : '^', ch ); buf[pos++] = (quote) ? '\\' : '^';
mb = ac_sprintf( buf + pos, ch < 0x100 ? "x%2X" : "u%4X", ch );
}
pos += mb; pos += mb;
} }
} }
@ -255,12 +257,13 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... )
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
if (mutex == NULL) if (mutex == NULL)
{ {
file = INVALID_HANDLE_VALUE; log_level = 0;
return; return;
} }
buf = HeapAlloc( hHeap, 0, 2048 ); buf = HeapAlloc( hHeap, 0, 2048 );
buf_len = (DWORD)HeapSize( hHeap, 0, buf ); buf_len = (DWORD)HeapSize( hHeap, 0, buf );
prefix_len = sprintf( buf, "%S (%lu): ", prog, GetCurrentProcessId() ); prefix_len = str_format( 0, TRUE, (DWORD_PTR)prog, 0 );
prefix_len += ac_sprintf(buf+prefix_len, " (%u): ", GetCurrentProcessId());
} }
if (WaitForSingleObject( mutex, 500 ) == WAIT_TIMEOUT) if (WaitForSingleObject( mutex, 500 ) == WAIT_TIMEOUT)
return; return;
@ -273,7 +276,6 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... )
if (file == INVALID_HANDLE_VALUE) if (file == INVALID_HANDLE_VALUE)
{ {
ReleaseMutex( mutex ); ReleaseMutex( mutex );
CloseHandle( mutex );
return; return;
} }
@ -287,18 +289,18 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... )
if (len != 0) if (len != 0)
{ {
memset( buf + 2, '=', 72 ); RtlFillMemory( buf + 2, 72, '=' );
buf[0] = buf[74] = buf[76] = '\r'; buf[0] = buf[74] = buf[76] = '\r';
buf[1] = buf[75] = buf[77] = '\n'; buf[1] = buf[75] = buf[77] = '\n';
WriteFile( file, buf, 78, &written, NULL ); WriteFile( file, buf, 78, &written, NULL );
} }
GetLocalTime( &now ); GetLocalTime( &now );
len = sprintf( buf, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d)" len = ac_sprintf( buf, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d)"
" started %d-%.2d-%.2d %d:%.2d:%.2d\r\n", " started %d-%2d-%2d %d:%2d:%2d\r\n",
log_level, log_level,
now.wYear, now.wMonth, now.wDay, now.wYear, now.wMonth, now.wDay,
now.wHour, now.wMinute, now.wSecond ); now.wHour, now.wMinute, now.wSecond );
WriteFile( file, buf, len, &written, NULL ); WriteFile( file, buf, len, &written, NULL );
if (szFormat == NULL) if (szFormat == NULL)
{ {
@ -369,16 +371,16 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... )
num = va_arg( pArgList, DWORD_PTR ); num = va_arg( pArgList, DWORD_PTR );
switch (*szFormat++) switch (*szFormat++)
{ {
case 'u': len += sprintf( buf + len, "%u", (DWORD)num ); break; case 'u': len += ac_sprintf( buf + len, "%u", (DWORD)num ); break;
case 'X': len += sprintf( buf + len, "%X", (DWORD)num ); break; case 'X': len += ac_sprintf( buf + len, "%X", (DWORD)num ); break;
case 'p': case 'p':
#ifdef _WIN64 #ifdef _WIN64
len += sprintf( buf + len, "%.8X_%.8X", len += ac_sprintf( buf + len, "%8X_%8X",
(DWORD)(num >> 32), (DWORD)num ); (DWORD)(num >> 32), (DWORD)num );
break; break;
#endif #endif
case 'q': len += sprintf( buf + len, "%.8X", (DWORD)num ); break; case 'q': len += ac_sprintf( buf + len, "%8X", (DWORD)num ); break;
case 'P': len += sprintf( buf + len, "00000000_%.8X", (DWORD)num ); break; case 'P': len += ac_sprintf( buf + len, "00000000_%8X", (DWORD)num ); break;
case 's': len = str_format( len, FALSE, num, slen ); break; case 's': len = str_format( len, FALSE, num, slen ); break;
case 'S': len = str_format( len, TRUE, num, slen ); break; case 'S': len = str_format( len, TRUE, num, slen ); break;
default: default:
@ -407,3 +409,263 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... )
CloseHandle( file ); CloseHandle( file );
ReleaseMutex( mutex ); ReleaseMutex( mutex );
} }
// Provide custom versions of used C runtime functions, to remove dependence on
// the runtime library.
// For my purposes (palette index and colors):
// * no leading space;
// * base is 10 or 16;
// * number doesn't overflow.
unsigned long ac_wcstoul( const wchar_t* str, wchar_t** end, int base )
{
unsigned c, n;
unsigned long num = 0;
for (;;)
{
n = -1;
c = *str;
if (c >= '0' && c <= '9')
n = c - '0';
else if (base == 16)
{
c |= 0x20;
if (c >= 'a' && c <= 'f')
n = c - 'a' + 10;
}
if (n == -1)
break;
num = num * base + n;
++str;
}
if (end != NULL)
*end = (wchar_t*)str;
return num;
}
// For my purposes (log level):
// * same as ac_wcstoul.
int ac_wtoi( const wchar_t* str )
{
return (int)ac_wcstoul( str, NULL, 10 );
}
// For my purposes (default attribute):
// * same as ac_wcstoul.
long ac_wcstol( const wchar_t* str, wchar_t** end, int base )
{
int neg = (*str == '-');
long num = ac_wcstoul( str + neg, end, base );
return neg ? -num : num;
}
// For my purposes (program separator):
// * set is only one or two characters.
wchar_t* ac_wcspbrk( const wchar_t* str, const wchar_t* set )
{
while (*str != '\0')
{
if (*str == set[0] || *str == set[1])
return (wchar_t*)str;
++str;
}
return NULL;
}
// For my purposes (path components):
// * c is not null.
wchar_t* ac_wcsrchr( const wchar_t* str, wchar_t c )
{
wchar_t* last = NULL;
while (*str != '\0')
{
if (*str == c)
last = (wchar_t*)str;
++str;
}
return last;
}
// For my purposes (import module matching):
// * A-Z becomes a-z;
// * s2 is lower case;
// * both strings are at least LEN long;
// * returns 0 for match, 1 for no match.
int ac_strnicmp( const char* s1, const char* s2, size_t len )
{
while (len--)
{
if (*s1 != *s2)
{
if (*s2 < 'a' || *s2 > 'z' || (*s1 | 0x20) != *s2)
return 1;
}
++s1;
++s2;
}
return 0;
}
static const char hex[16] = { '0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F' };
// For my purposes:
// * BUF is big enough;
// * FMT is valid;
// * width implies zero fill and the number is not bigger than the width;
// * only types d, u & X are supported, all as 32-bit unsigned;
// * BUF is NOT NUL-terminated.
int ac_sprintf( char* buf, const char* fmt, ... )
{
va_list args;
DWORD num;
int t, width;
char* beg = buf;
va_start( args, fmt );
while (*fmt)
{
t = *fmt++;
if (t != '%')
*buf++ = t;
else
{
num = va_arg( args, DWORD );
t = *fmt++;
width = 0;
if (t == '2' || t == '4' || t == '8')
{
width = t - '0';
t = *fmt++;
}
if (t == 'X')
{
int bits;
if (width == 0)
{
if (num & 0xF0000000)
bits = 32;
else
{
bits = 4;
while (num >> bits)
bits += 4;
}
}
else
bits = width * 4;
do
{
bits -= 4;
*buf++ = hex[num >> bits & 0xF];
} while (bits);
}
else // (t == 'd' || t == 'u')
{
if (width == 2)
{
*buf++ = (int)(num / 10) + '0';
*buf++ = (int)(num % 10) + '0';
}
else
{
unsigned power;
if (num >= 1000000000)
power = 1000000000;
else
{
power = 1;
while (num / (power * 10))
power *= 10;
}
do
{
*buf++ = (unsigned)(num / power) % 10 + '0';
power /= 10;
} while (power);
}
}
}
}
return (int)(buf - beg);
}
// For my purposes:
// * 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;
// * c is not output if NUL;
// * no other type is used;
// * return value is not used.
int ac_wprintf( wchar_t* buf, const char* fmt, ... )
{
va_list args;
DWORD num;
int t;
va_start( args, fmt );
while (*fmt)
{
t = *fmt++;
if (t != '%')
*buf++ = t;
else
{
num = va_arg( args, DWORD );
t = *fmt++;
if (t == '2')
{
++fmt;
*buf++ = hex[num >> 4];
*buf++ = hex[num & 0xF];
}
else if (t == 'X')
{
int bits = 4;
while (num >> bits)
bits += 4;
do
{
bits -= 4;
*buf++ = hex[num >> bits & 0xF];
} while (bits);
}
else if (t == 'c')
{
if (num)
*buf++ = (wchar_t)num;
}
else // (t == 'd' || t == 'u')
{
int power = 10;
while (num / power)
power *= 10;
do
{
power /= 10;
*buf++ = (int)(num / power) % 10 + '0';
} while (power != 1);
}
}
}
*buf = '\0';
return 0;
}