diff --git a/ANSI.c b/ANSI.c index 6e74805..4e54c01 100644 --- a/ANSI.c +++ b/ANSI.c @@ -197,14 +197,17 @@ v1.83, 16 February, 2018: 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; dynamically load WINMM.DLL; use sprintf/_snprintf/_snwprintf instead of wsprintf, avoiding USER32.DLL; replace bsearch (in procrva.c) with specific code; if the primary thread is detached exit the process; 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" @@ -445,6 +448,8 @@ typedef struct BYTE reverse; // swap console foreground & background attributes } SGR; +SGR orgsgr; // original SGR + typedef struct { SGR sgr, SaveSgr; @@ -493,7 +498,7 @@ void get_state( void ) 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, 0, sizeof(STATE), buf ); init = (GetLastError() != ERROR_ALREADY_EXISTS); @@ -518,11 +523,11 @@ void get_state( void ) Info.dwSize = csbix.dwSize; ATTR = csbix.wAttributes; WIN = csbix.srWindow; - memcpy( pState->o_palette, csbix.ColorTable, sizeof(csbix.ColorTable) ); + arrcpy( pState->o_palette, csbix.ColorTable ); } else { - memcpy( pState->o_palette, legacy_palette, sizeof(legacy_palette) ); + arrcpy( pState->o_palette, legacy_palette ); if (!GetConsoleScreenBufferInfo( hConOut, &Info )) { DEBUGSTR( 1, "Failed to get screen buffer info (%u) - assuming defaults", @@ -536,35 +541,32 @@ void get_state( void ) BOTTOM = 24; } } - memcpy( pState->x_palette, xterm_palette, sizeof(xterm_palette) ); - if (GetEnvironmentVariable( L"ANSICON_REVERSE", NULL, 0 )) + arrcpy( pState->x_palette, xterm_palette ); + + 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 ); - pState->sgr.reverse = TRUE; - 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 ); + *a++ = '-'; + ATTR = ((ATTR >> 4) & 15) | ((ATTR & 15) << 4); } + ac_wprintf( a, "%X", ATTR & 255 ); + SetEnvironmentVariable( L"ANSICON_DEF", def ); set_ansicon( &Info ); CloseHandle( hConOut ); } @@ -611,7 +613,7 @@ BOOL search_env( LPCTSTR var, LPCTSTR val ) break; } } while (*end != '\0'); - if (_wcsicmp( val, var ) == 0) + if (lstrcmpi( val, var ) == 0) return !not; } @@ -1002,7 +1004,7 @@ void SendSequence( LPTSTR seq ) DWORD len; 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) return; for (len = 0; *seq; len += 2, ++seq) @@ -1028,9 +1030,9 @@ void send_palette_sequence( COLORREF c ) g = GetGValue( c ); b = GetBValue( c ); 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 - _snwprintf( buf, lenof(buf), L"#%02X%02X%02X", r, g, b ); + ac_wprintf( buf, "#%2X%2X%2X", r, g, b ); SendSequence( buf ); } @@ -1040,7 +1042,7 @@ void init_tabs( int size ) { int i; - memset( pState->tab_stop, FALSE, MAX_TABS ); + RtlZeroMemory( pState->tab_stop, MAX_TABS ); for (i = 0; i < MAX_TABS; i += size) pState->tab_stop[i] = TRUE; pState->tabs = TRUE; @@ -1130,12 +1132,12 @@ void Reset( BOOL hard ) csbix.cbSize = sizeof(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.Bottom; 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; *def = '7'; def[1] = '\0'; 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; if (a < 0) { @@ -1696,7 +1700,7 @@ void InterpretEscSeq( void ) return; case 3: // ESC[3g Clear all tabs - memset( pState->tab_stop, FALSE, MAX_TABS ); + RtlZeroMemory( pState->tab_stop, MAX_TABS ); pState->tabs = TRUE; return; @@ -1775,9 +1779,9 @@ void InterpretEscSeq( void ) case 6: // ESC[6n Report cursor position { TCHAR buf[32]; - _snwprintf( buf, lenof(buf), L"\33[%d;%d%sR", - CUR.Y - top + 1, CUR.X + 1, - (suffix2 == '+') ? L"+" : L"" ); + ac_wprintf( buf, "\33[%d;%d%cR", + CUR.Y - top + 1, CUR.X + 1, + (suffix2 == '+') ? '+' : '\0' ); SendSequence( buf ); } return; @@ -1904,7 +1908,7 @@ void InterpretEscSeq( void ) BOOL started = FALSE; 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) break; if (end[2] == ';' || end[2] == '\0') @@ -1957,7 +1961,7 @@ void InterpretEscSeq( void ) if (*beg == '#') { valid = TRUE; - c = (DWORD)wcstoul( ++beg, &end, 16 ); + c = (DWORD)ac_wcstoul( ++beg, &end, 16 ); if (end - beg == 3) { r = (BYTE)(c >> 8); @@ -1976,18 +1980,18 @@ void InterpretEscSeq( void ) else valid = FALSE; } - else if (wcsncmp( beg, L"rgb:", 4 ) == 0) + else if (memcmp( beg, L"rgb:", 8 ) == 0) { 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)) { 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)) { 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') && (end - beg == 2 || end - beg == 4)) { @@ -2000,15 +2004,15 @@ void InterpretEscSeq( void ) else { valid = FALSE; - c = (DWORD)wcstoul( beg, &end, 10 ); + c = (DWORD)ac_wcstoul( beg, &end, 10 ); if (*end == ',' && c < 256) { r = (BYTE)c; - c = (DWORD)wcstoul( end + 1, &end, 10 ); + c = (DWORD)ac_wcstoul( end + 1, &end, 10 ); if (*end == ',' && c < 256) { 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) { b = (BYTE)c; @@ -2043,15 +2047,15 @@ void InterpretEscSeq( void ) // Reset each index, or the entire palette. if (Pt_len == 0) { - memcpy(csbix.ColorTable, pState->o_palette, sizeof(csbix.ColorTable)); - memcpy( pState->x_palette, xterm_palette, sizeof(xterm_palette) ); + arrcpy( csbix.ColorTable, pState->o_palette ); + arrcpy( pState->x_palette, xterm_palette ); } else { LPTSTR beg, end; 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) break; if (i < 16) @@ -2575,12 +2579,13 @@ ParseAndPrintString( HANDLE hDev, // - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. const char APIKernel[] = "kernel32.dll"; -const char APIConsole[] = "API-MS-Win-Core-Console-"; -const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-"; -const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-"; -const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-"; -const char APIFile[] = "API-MS-Win-Core-File-"; -const char APIHandle[] = "API-MS-Win-Core-Handle-"; +const char APIcore[] = "api-ms-win-core-"; +const char APIConsole[] = "console-"; +const char APIProcessThreads[] = "processthreads-"; +const char APIProcessEnvironment[] = "processenvironment-"; +const char APILibraryLoader[] = "libraryloader-"; +const char APIFile[] = "file-"; +const char APIHandle[] = "handle-"; typedef struct { @@ -2686,15 +2691,20 @@ BOOL HookAPIOneMod( // for the module whose name matches the pszFunctionModule parameter. for (; pImportDesc->Name; pImportDesc++) { - BOOL kernel = TRUE; + BOOL kernel = EOF; PSTR pszModName = MakeVA( PSTR, pImportDesc->Name ); - if (_strnicmp( pszModName, APIKernel, 8 ) != 0 || - (_stricmp( pszModName+8, APIKernel+8 ) != 0 && pszModName[8] != '\0')) + if (ac_strnicmp( pszModName, APIKernel, 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; 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) { @@ -2703,16 +2713,16 @@ BOOL HookAPIOneMod( if (hook->lib == lib->name) hook->apifunc = GetProcAddress( lib->base, hook->name ); } + kernel = FALSE; break; } } - if (lib->name == NULL) - { - if (log_level & 16) - DEBUGSTR( 2, " %s%s %s", sp, zIgnoring, pszModName ); - continue; - } - kernel = FALSE; + } + if (kernel == EOF) + { + if (log_level & 16) + DEBUGSTR( 2, " %s%s %s", sp, zIgnoring, pszModName ); + continue; } if (log_level & 16) 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, BOOL wide, LPCVOID lpApp, LPCVOID lpCmd ) { - app[MAX_DEV_PATH-1] = '\0'; - if (lpApp == NULL) { typedef DWORD (WINAPI *PGPIFNW)( HANDLE, LPTSTR, DWORD ); @@ -2905,7 +2913,7 @@ static LPTSTR get_program( LPTSTR app, HANDLE hProcess, term = L"\""; ++pos; } - wcsncpy( app, pos, MAX_DEV_PATH-1 ); + lstrcpyn( app, pos, MAX_DEV_PATH ); } else { @@ -2916,13 +2924,13 @@ static LPTSTR get_program( LPTSTR app, HANDLE hProcess, term = L"\""; ++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, // 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 // "a name.exe") which I won't do. - name = wcspbrk( app, term ); + name = ac_wcspbrk( app, term ); if (name != NULL) *name = '\0'; } @@ -2930,9 +2938,9 @@ static LPTSTR get_program( LPTSTR app, HANDLE hProcess, else { if (wide) - wcsncpy( app, lpApp, MAX_DEV_PATH-1 ); + lstrcpyn( app, lpApp, MAX_DEV_PATH ); 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 ); } @@ -2994,8 +3002,8 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, TCHAR args[64]; STARTUPINFO si; PROCESS_INFORMATION pi; - wcscpy( DllNameType, L"CON.exe" ); - _snwprintf( args, lenof(args), L"ansicon -P%lu", child_pi->dwProcessId ); + memcpy( DllNameType, L"CON.exe", 16 ); + ac_wprintf( args, "ansicon -P%u", child_pi->dwProcessId ); ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); if (CreateProcess( DllName, args, NULL, NULL, FALSE, 0, NULL, NULL, @@ -3007,7 +3015,7 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi, } else DEBUGSTR( 1, "Could not execute %\"S (%u)", DllName, GetLastError() ); - wcscpy( DllNameType, L"32.dll" ); + memcpy( DllNameType, L"32.dll", 14 ); } else #endif @@ -3413,7 +3421,7 @@ WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer, if (mb_size <= 4 && mb_size > len - pos) { mb_len = len - pos; - memcpy( mb, aBuf + pos, mb_len ); + RtlMoveMemory( mb, aBuf + pos, mb_len ); len = pos; if (log_level & 4) { @@ -3569,10 +3577,13 @@ WINAPI MyCreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess, { 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$"; - if (_stricmp( lpFileName, "CONOUT$" ) == 0) dwDesiredAccess |= GENERIC_READ; + } } return CreateFileA( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, @@ -3588,10 +3599,22 @@ WINAPI MyCreateFileW( LPCWSTR lpFileName, DWORD dwDesiredAccess, { 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$"; - if (_wcsicmp( lpFileName, L"CONOUT$" ) == 0) dwDesiredAccess |= GENERIC_READ; + } } return CreateFileW( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, @@ -3708,7 +3731,7 @@ void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi ) 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->srWindow.Right - pcsbi->srWindow.Left + 1, pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1 ); @@ -3718,7 +3741,7 @@ void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi ) DWORD WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) { - if (_stricmp( lpName, "ANSICON_VER" ) == 0) + if (lstrcmpiA( lpName, "ANSICON_VER" ) == 0) { if (nSize < sizeof(PVEREA)) return sizeof(PVEREA); @@ -3726,7 +3749,7 @@ WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) return sizeof(PVEREA) - 1; } - if (_stricmp( lpName, "CLICOLOR" ) == 0) + if (lstrcmpiA( lpName, "CLICOLOR" ) == 0) { if (nSize < 2) return 2; @@ -3735,7 +3758,7 @@ WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) return 1; } - if (_stricmp( lpName, "ANSICON" ) == 0) + if (lstrcmpiA( lpName, "ANSICON" ) == 0) set_ansicon( NULL ); return GetEnvironmentVariableA( lpName, lpBuffer, nSize ); @@ -3744,7 +3767,7 @@ WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ) DWORD 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)) return lenof(PVERE); @@ -3752,7 +3775,7 @@ WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize ) return lenof(PVERE) - 1; } - if (_wcsicmp( lpName, L"CLICOLOR" ) == 0) + if (lstrcmpi( lpName, L"CLICOLOR" ) == 0) { if (nSize < 2) return 2; @@ -3761,7 +3784,7 @@ WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize ) return 1; } - if (_wcsicmp( lpName, L"ANSICON" ) == 0) + if (lstrcmpi( lpName, L"ANSICON" ) == 0) set_ansicon( NULL ); return GetEnvironmentVariableW( lpName, lpBuffer, nSize ); @@ -3832,12 +3855,14 @@ void OriginalAttr( PVOID lpReserved ) { HANDLE hConOut; CONSOLE_SCREEN_BUFFER_INFO Info; + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNTHeader; + BOOL 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; + get_state(); + + pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( NULL ); + pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); // 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- @@ -3845,27 +3870,35 @@ void OriginalAttr( PVOID lpReserved ) // be dynamic due to lack of the IAT. if (lpReserved == NULL) { - BOOL dynamic = TRUE; - PIMAGE_DOS_HEADER pDosHeader; - PIMAGE_NT_HEADERS pNTHeader; - pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( NULL ); - pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); + org = TRUE; #ifdef _WIN64 if (pNTHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) - dynamic = FALSE; + org = FALSE; else #endif if (pNTHeader->DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT && get_os_version() >= 0x602) - dynamic = FALSE; - if (dynamic) - orgattr = ATTR; + org = FALSE; + } + 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 ); GetConsoleCursorInfo( hConOut, &orgcci ); + CloseHandle( hConOut ); } - CloseHandle( hConOut ); - - get_state(); } @@ -3888,8 +3921,6 @@ DWORD WINAPI exit_thread( LPVOID lpParameter ) // 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 bResult = TRUE; @@ -3901,7 +3932,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) if (dwReason == DLL_PROCESS_ATTACH) { - hHeap = HeapCreate( 0, 0, 128 * 1024 ); + hHeap = HeapCreate( 0, 0, 256 * 1024 ); hKernel = GetModuleHandleA( APIKernel ); GetConsoleScreenBufferInfoX = (PHCSBIX)GetProcAddress( hKernel, "GetConsoleScreenBufferInfoEx" ); @@ -3914,7 +3945,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) *logstr = '\0'; GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) ); - log_level = _wtoi( logstr ); + log_level = ac_wtoi( logstr ); prog = get_program_name( NULL ); #if defined(_WIN64) || defined(W32ON64) DllNameType = DllName - 6 + @@ -3982,6 +4013,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) SetConsoleMode( hConOut, orgmode ); SetConsoleCursorInfo( hConOut, &orgcci ); CloseHandle( hConOut ); + pState->sgr = orgsgr; } if (hMap != NULL) { diff --git a/ansicon.c b/ansicon.c index f958ebe..03f950d 100644 --- a/ansicon.c +++ b/ansicon.c @@ -89,9 +89,12 @@ v1.80, 28 October & 30 November, 2017: write newline with _putws, not putwchar (fixes redirecting to CON); 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 "version.h" @@ -128,25 +131,6 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR ); static HANDLE hConOut; -static WORD wAttr; - -void get_original_attr( void ) -{ - CONSOLE_SCREEN_BUFFER_INFO csbi; - - hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, 0 ); - GetConsoleScreenBufferInfo( hConOut, &csbi ); - wAttr = csbi.wAttributes; -} - - -void set_original_attr( void ) -{ - SetConsoleTextAttribute( hConOut, wAttr ); - CloseHandle( hConOut ); -} // The fputws function in MSVCRT.DLL (Windows 7 x64) is broken for Unicode @@ -172,53 +156,6 @@ int my_fputws( const wchar_t* s, FILE* f ) #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. void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) { @@ -227,7 +164,6 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) PBYTE proc; DWORD rva; BOOL fOk; - DWORD len; LPVOID param; HANDLE thread; DWORD ticks; @@ -236,7 +172,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) int type; #endif - DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId ); + DEBUGSTR( 1, "Parent = %S (%u)", app, ppi->dwProcessId ); // Find the base address of kernel32.dll. ticks = GetTickCount(); @@ -266,11 +202,13 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) return; } proc = param = NULL; - len = (DWORD)(prog - prog_path); - memcpy( DllName, prog_path, TSIZE(len) ); #ifdef _WIN64 - type = (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) ? 32 : 64; - _snwprintf( DllName + len, MAX_PATH-1 - len, L"ANSI%d.dll", type ); + type = 64; + if (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) + { + type = 32; + *(PDWORD)DllNameType = 0x320033/*L'23'*/; + } #endif me.dwSize = sizeof(MODULEENTRY32); 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) { proc = me.modBaseAddr; - if (!unload) + if (!unload || param) break; } else if (unload) { #ifdef _WIN64 - if (_wcsicmp( me.szModule, DllName + len ) == 0) + if (_wcsicmp( me.szModule, DllNameType - 4 ) == 0) #else if (_wcsicmp( me.szModule, L"ANSI32.dll" ) == 0) #endif + { param = me.modBaseAddr; + if (proc) + break; + } } } CloseHandle( hSnap ); @@ -307,7 +249,6 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) rva = GetProcRVA( L"kernel32.dll", (unload) ? "FreeLibrary" : "LoadLibraryW", type ); #else - wcscpy( DllName + len, L"ANSI32.dll" ); rva = GetProcRVA( L"kernel32.dll", unload ? "FreeLibrary" : "LoadLibraryW" ); #endif if (rva == 0) @@ -316,13 +257,14 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) if (!unload) { + DWORD len = TSIZE((DWORD)wcslen( DllName ) + 1); param = VirtualAllocEx(ppi->hProcess, NULL, len, MEM_COMMIT,PAGE_READWRITE); if (param == NULL) { DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError()); goto no_go; } - WriteProcMem( param, DllName, TSIZE(len + 11) ); + WriteProcMem( param, DllName, len ); } thread = CreateRemoteThread( ppi->hProcess, NULL, 4096, (LPTHREAD_START_ROUTINE)proc, param, 0, NULL ); @@ -346,7 +288,6 @@ int main( void ) LPTSTR argv, arg, cmd; TCHAR buf[4]; BOOL shell, run, gui; - HMODULE ansi; DWORD len; int rc = 0; @@ -361,7 +302,9 @@ int main( void ) _setmode( 2, _O_U16TEXT); // 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(); 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'; GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) ); log_level = _wtoi( buf ); @@ -405,7 +345,12 @@ int main( void ) } 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 ); } return 0; @@ -456,20 +401,8 @@ int main( void ) } case 'm': - { - int a = wcstol( arg + 2, NULL, 16 ); - if (a == 0) - a = (arg[2] == '-') ? -7 : 7; - if (a < 0) - { - SetEnvironmentVariable( L"ANSICON_REVERSE", L"1" ); - a = -a; - a = ((a >> 4) & 15) | ((a & 15) << 4); - } - SetConsoleTextAttribute( hConOut, (WORD)a ); - SetEnvironmentVariable( L"ANSICON_DEF", NULL ); + SetEnvironmentVariable( L"ANSICON_DEF", arg[2] ? arg + 2 : L"7" ); break; - } case 'e': case 'E': @@ -494,13 +427,7 @@ arg_out: } // Ensure the default attributes are the current attributes. - if (GetEnvironmentVariable( L"ANSICON_DEF", buf, lenof(buf) ) != 0) - { - int a = wcstol( buf, NULL, 16 ); - if (a < 0) - a = ((-a >> 4) & 15) | ((-a & 15) << 4); - SetConsoleTextAttribute( hConOut, (WORD)a ); - } + WriteConsole( hConOut, L"\33[m", 3, &len, NULL ); if (run) { @@ -518,11 +445,9 @@ arg_out: ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); - if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, - NULL, NULL, &si, &pi )) + if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi )) { - Inject( &pi, &gui, arg ); - ResumeThread( pi.hThread ); + ProcessType( &pi, NULL, &gui ); if (!gui) { SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); @@ -540,13 +465,7 @@ arg_out: } else if (*arg) { - ansi = LoadLibrary( ANSIDLL ); - if (ansi == NULL) - { - print_error( ANSIDLL ); - rc = 1; - } - else if (*arg == 'e' || *arg == 'E') + if (*arg == 'e' || *arg == 'E') { cmd += 2; if (*cmd == ' ' || *cmd == '\t') @@ -574,11 +493,8 @@ arg_out: get_file( arg, &argv, &cmd ); } while (*arg); } - FreeLibrary( ansi ); } - set_original_attr(); - return rc; } diff --git a/ansicon.h b/ansicon.h index b71e894..1af6619 100644 --- a/ansicon.h +++ b/ansicon.h @@ -83,34 +83,62 @@ typedef struct IMAGE_COR20_HEADER #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 ); +#ifdef _WIN64 +EXTERN +#endif void InjectDLL( LPPROCESS_INFORMATION, PBYTE ); void RemoteLoad32( LPPROCESS_INFORMATION ); #ifdef _WIN64 void InjectDLL32( LPPROCESS_INFORMATION, PBYTE ); -void RemoteLoad64( LPPROCESS_INFORMATION ); -DWORD GetProcRVA( LPCTSTR, LPCSTR, int ); +EXTERN void RemoteLoad64( LPPROCESS_INFORMATION ); +EXTERN DWORD GetProcRVA( LPCTSTR, LPCSTR, int ); #else -DWORD GetProcRVA( LPCTSTR, LPCSTR ); +EXTERN DWORD GetProcRVA( LPCTSTR, LPCSTR ); #endif extern HANDLE hHeap; -extern TCHAR prog_path[MAX_PATH]; +EXTERN TCHAR prog_path[MAX_PATH]; extern LPTSTR prog; LPTSTR get_program_name( LPTSTR ); -extern TCHAR DllName[MAX_PATH]; -extern LPTSTR DllNameType; +EXTERN TCHAR DllName[MAX_PATH]; +EXTERN LPTSTR DllNameType; extern char ansi_dll[MAX_PATH]; extern DWORD ansi_len; extern char* ansi_bits; void set_ansi_dll( void ); DWORD get_os_version( void ); -extern int log_level; -void DEBUGSTR( int level, LPCSTR szFormat, ... ); +EXTERN int log_level; +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 diff --git a/injdll.c b/injdll.c index 464456d..a0278b3 100644 --- a/injdll.c +++ b/injdll.c @@ -117,7 +117,7 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase ) ip.pL = (PLONG_PTR)pImports; *ip.pL++ = IMAGE_ORDINAL_FLAG + 1; *ip.pL++ = 0; - memcpy( ip.pB, ansi_dll, ansi_len ); + RtlMoveMemory( ip.pB, ansi_dll, ansi_len ); ip.pB += ansi_len; ip.pI->OriginalFirstThunk = 0; ip.pI->TimeDateStamp = 0; @@ -213,7 +213,7 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase ) ip.pL = (PLONG)pImports; *ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1; *ip.pL++ = 0; - memcpy( ip.pB, ansi_dll, ansi_len ); + RtlMoveMemory( ip.pB, ansi_dll, ansi_len ); ip.pB += ansi_len; ip.pI->OriginalFirstThunk = 0; ip.pI->TimeDateStamp = 0; @@ -331,7 +331,7 @@ void RemoteLoad64( LPPROCESS_INFORMATION ppi ) return; } - len = (DWORD)TSIZE(wcslen( DllName ) + 1); + len = (DWORD)TSIZE(lstrlen( DllName ) + 1); ip.pB = code; *ip.pL++ = ntdll + rLdrLoadDll; // address of LdrLoadDll @@ -400,7 +400,7 @@ void RemoteLoad32( LPPROCESS_INFORMATION ppi ) } bMem = PtrToUint( pMem ); - len = (DWORD)TSIZE(wcslen( DllName ) + 1); + len = (DWORD)TSIZE(lstrlen( DllName ) + 1); ip.pB = code; *ip.pS++ = 0x5451; // push ecx esp diff --git a/makefile.vc b/makefile.vc index 1427cad..7679063 100644 --- a/makefile.vc +++ b/makefile.vc @@ -22,6 +22,9 @@ # 30 April, 2018: # use undocumented rc option /s to remove its logo; # 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 = 64 @@ -39,20 +42,26 @@ DIR = x86 !ELSE !IF $(BITS) == 64 DIR = x64 -RFLAGS = /D_WIN64 !ELSE !ERROR BITS should be defined to 32 or 64. !ENDIF !ENDIF -# This is required for the 2003 Platform SDK, but not for Visual Studio 2010. -!IF "$(_NMAKE_VER)" == "7.00.8882" -!IF $(BITS) == 64 -LIBS64 = bufferoverflowu.lib +# Disable security checks, but VC6 & 7 don't have /GS-. +!IF "$(_NMAKE_VER)" == "7.00.8882" && $(BITS) == 32 +NOSECCHK = +RFLAGS = # The 2003 Toolkit doesn't have MSVCRT.LIB, but VC98 does. -!ELSEIF !DEFINED(SHARE) && !DEFINED(MSVCDIR) +!IF !DEFINED(SHARE) && !DEFINED(MSVCDIR) SHARE = !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 # Link with MSVCRT.LIB by default. @@ -63,9 +72,14 @@ SHARE = /MD # Manifest tool to embed the manifest required by 2008. MT = mt.exe -RFLAGS = /s -CFLAGS = /nologo /W3 /O2 $(SHARE) /D_CRT_SECURE_NO_WARNINGS -LIBS = advapi32.lib $(LIBS64) +!IFNDEF RFLAGS +RFLAGS = /nologo +!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. LINK = /link /version:20033.18771 @@ -92,14 +106,14 @@ MTmsg = @echo Embedding manifest& 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: 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 !IF "$(_NMAKE_VER)" == "9.00.30729.01" $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1 @@ -107,30 +121,23 @@ x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res !ENDIF x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res - $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ - /base:0xAC0000 /filealign:512 -!IF "$(_NMAKE_VER)" == "9.00.30729.01" - $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 - @del $@.manifest -!ENDIF + $(LDmsg)$(CC) /nologo /LD /Fe$@ $** $(LIBS) /link \ + /base:0xAC0000 /entry:DllMain /filealign:512 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) x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res - $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ - /base:0xAC000000 + $(LDmsg)$(CC) /nologo /LD /Fe$@ $** $(LIBS) /link \ + /base:0xAC000000 /entry:DllMain x64\ANSI32.dll: x64\ANSI32.obj $(X6432OBJS) x86\ansi.res - $(LDmsg)$(CC) /nologo $(SHARE) /LD /Fe$@ $** $(LIBS) /link \ - /base:0xAC0000 /filealign:512 /largeaddressaware -!IF "$(_NMAKE_VER)" == "9.00.30729.01" - $(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;2 - @del $@.manifest -!ENDIF + $(LDmsg)$(CC) /nologo /LD /Fe$@ $** $(LIBS) /link \ + /base:0xAC0000 /entry:DllMain /filealign:512 \ + /largeaddressaware ansicon.c: ansicon.h version.h ansicon.rc: version.h @@ -141,6 +148,9 @@ injdll.c: ansicon.h proctype.c: ansicon.h procrva.c: ansicon.h +$(DIR)\ansicon.obj: + $(CCmsg)$(CC) /c $(CFLAGS) $(SHARE) /Fo$@ $? + x64\ANSI32.obj: ANSI.c $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? diff --git a/procrva.c b/procrva.c index fa73917..87f79c4 100644 --- a/procrva.c +++ b/procrva.c @@ -32,7 +32,7 @@ DWORD GetProcRVA( LPCTSTR module, LPCSTR func ) #endif len = GetSystemDirectory( buf, MAX_PATH ); buf[len++] = '\\'; - wcscpy( buf + len, module ); + lstrcpy( buf + len, module ); hMod = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE ); if (hMod == NULL) { diff --git a/proctype.c b/proctype.c index 7bb6d43..5c72be6 100644 --- a/proctype.c +++ b/proctype.c @@ -46,6 +46,22 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui ) MEMORY_BASIC_INFORMATION minfo; IMAGE_DOS_HEADER dos_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; *gui = FALSE; @@ -62,7 +78,7 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui ) && nt_header.Signature == IMAGE_NT_SIGNATURE && !(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' nt_header.OptionalHeader.MinorImageVersion == 18771) // 'SI' return -1; @@ -102,27 +118,27 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui ) #endif } } - DEBUGSTR( 1, " 32-bit %s (base = %q)", - (*gui) ? "GUI" : "console", minfo.BaseAddress ); + if (!skip_log) + DEBUGSTR( 1, " 32-bit %s (base = %q)", + (*gui) ? "GUI" : "console", minfo.BaseAddress ); return 32; } if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { #ifdef _WIN64 - DEBUGSTR( 1, " 64-bit %s (base = %p)", - (*gui) ? "GUI" : "console", minfo.BaseAddress ); - return 64; -#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 ); + if (!skip_log) + DEBUGSTR( 1, " 64-bit %s (base = %p)", + (*gui) ? "GUI" : "console", minfo.BaseAddress ); return 64; #else DEBUGSTR( 1, " 64-bit %s (base = %P)", (*gui) ? "GUI" : "console", minfo.BaseAddress ); +#if defined(W32ON64) + return 64; +#else DEBUGSTR( 1, " Unsupported (use x64\\ansicon)" ); return 0; +#endif #endif } DEBUGSTR( 1, " Ignoring unsupported machine (0x%X)", diff --git a/readme.txt b/readme.txt index 7f6dc48..bed6158 100644 --- a/readme.txt +++ b/readme.txt @@ -339,13 +339,16 @@ Version History Legend: + added, - bug-fixed, * changed. - 1.84-wip - 4 May, 2018: + 1.84-wip - 7 May, 2018: - close the flush handles on detach; - WriteFile wasn't properly testing if its handle was for a console; - 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 - that return, rather than call ExitProcess). + that return, rather than call ExitProcess); + * ansicon.exe statically loads the DLL. 1.83 - 16 February, 2018: - create the flush thread on first use. @@ -621,4 +624,4 @@ Distribution ======================== -Jason Hood, 4 May, 2018. +Jason Hood, 7 May, 2018. diff --git a/util.c b/util.c index 9bbaa00..bc8c819 100644 --- a/util.c +++ b/util.c @@ -30,12 +30,12 @@ LPTSTR get_program_name( LPTSTR program ) GetModuleFileName( NULL, prog_path, lenof(prog_path) ); program = prog_path; } - name = wcsrchr( program, '\\' ); + name = ac_wcsrchr( program, '\\' ); if (name != NULL) ++name; else name = program; - ext = wcsrchr( name, '.' ); + ext = ac_wcsrchr( name, '.' ); if (ext != NULL && ext != name) *ext = '\0'; @@ -107,13 +107,13 @@ static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len ) src.a = (LPSTR)str; 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) { LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + len * 6 + 8 ); if (tmp == NULL) - return 0; + return pos; buf = tmp; 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 27 : buf[pos++] = 'e'; break; default: - pos += sprintf( buf + pos, "x%.2X", ch ); + pos += ac_sprintf( buf + pos, "x%2X", ch ); } } else @@ -214,7 +214,7 @@ static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len ) } } if (quote && start_trail) - pos += sprintf( buf + pos, "\\x%.2X", ch ); + pos += ac_sprintf( buf + pos, "\\x%2X", ch ); else 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, NULL, pDef ); 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; } } @@ -255,12 +257,13 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... ) mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); if (mutex == NULL) { - file = INVALID_HANDLE_VALUE; + log_level = 0; return; } buf = HeapAlloc( hHeap, 0, 2048 ); 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) return; @@ -273,7 +276,6 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... ) if (file == INVALID_HANDLE_VALUE) { ReleaseMutex( mutex ); - CloseHandle( mutex ); return; } @@ -287,18 +289,18 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... ) if (len != 0) { - memset( buf + 2, '=', 72 ); + RtlFillMemory( buf + 2, 72, '=' ); buf[0] = buf[74] = buf[76] = '\r'; buf[1] = buf[75] = buf[77] = '\n'; WriteFile( file, buf, 78, &written, NULL ); } GetLocalTime( &now ); - len = sprintf( buf, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d)" - " started %d-%.2d-%.2d %d:%.2d:%.2d\r\n", - log_level, - now.wYear, now.wMonth, now.wDay, - now.wHour, now.wMinute, now.wSecond ); + len = ac_sprintf( buf, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d)" + " started %d-%2d-%2d %d:%2d:%2d\r\n", + log_level, + now.wYear, now.wMonth, now.wDay, + now.wHour, now.wMinute, now.wSecond ); WriteFile( file, buf, len, &written, NULL ); if (szFormat == NULL) { @@ -369,16 +371,16 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... ) num = va_arg( pArgList, DWORD_PTR ); switch (*szFormat++) { - case 'u': len += sprintf( buf + len, "%u", (DWORD)num ); break; - case 'X': len += sprintf( buf + len, "%X", (DWORD)num ); break; + case 'u': len += ac_sprintf( buf + len, "%u", (DWORD)num ); break; + case 'X': len += ac_sprintf( buf + len, "%X", (DWORD)num ); break; case 'p': #ifdef _WIN64 - len += sprintf( buf + len, "%.8X_%.8X", - (DWORD)(num >> 32), (DWORD)num ); + len += ac_sprintf( buf + len, "%8X_%8X", + (DWORD)(num >> 32), (DWORD)num ); break; #endif - case 'q': len += sprintf( buf + len, "%.8X", (DWORD)num ); break; - case 'P': len += sprintf( buf + len, "00000000_%.8X", (DWORD)num ); break; + case 'q': len += ac_sprintf( buf + len, "%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, TRUE, num, slen ); break; default: @@ -407,3 +409,263 @@ void DEBUGSTR( int level, LPCSTR szFormat, ... ) CloseHandle( file ); 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; +}