From 33ba31ad3c9d7d89ffca47834958966eea89cc82 Mon Sep 17 00:00:00 2001 From: Jason Hood Date: Mon, 7 May 2018 10:31:41 +1000 Subject: [PATCH] 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. --- ANSI.c | 264 ++++++++++++++++++++++++-------------------- ansicon.c | 150 ++++++------------------- ansicon.h | 46 ++++++-- injdll.c | 8 +- makefile.vc | 64 ++++++----- procrva.c | 2 +- proctype.c | 38 +++++-- readme.txt | 11 +- util.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++++---- 9 files changed, 579 insertions(+), 312 deletions(-) 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; +}