├── .gitignore ├── G1.txt ├── acrt.def ├── version.h ├── G1.bat ├── ansi.rc ├── LICENSE.txt ├── readme.md ├── ansicon.rc ├── procrva.c ├── palette.h ├── makefile.gcc ├── makefile.vc ├── ansicon.h ├── proctype.c ├── sequences.txt ├── injdll.c ├── util.c ├── ansicon.c └── readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | /x86 3 | /x64 4 | -------------------------------------------------------------------------------- /G1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adoxa/ansicon/HEAD/G1.txt -------------------------------------------------------------------------------- /acrt.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | __iob_func 3 | _fileno 4 | _get_osfhandle 5 | _iob 6 | _isatty 7 | _setmode 8 | _snwprintf 9 | _wcsicmp 10 | _wtoi 11 | fflush 12 | fputws 13 | free 14 | fwprintf 15 | iswctype 16 | malloc 17 | memchr 18 | memcpy 19 | memmove 20 | qsort 21 | setlocale 22 | sprintf 23 | towlower 24 | wcschr 25 | wcscmp 26 | wcscpy 27 | wcslen 28 | wcspbrk 29 | wcsstr 30 | wprintf 31 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | /* 2 | version.h - Version defines. 3 | */ 4 | 5 | #define PVERS L"1.89" // wide string 6 | #define PVERSA "1.89" // ANSI string (windres 2.16.91 didn't like L) 7 | #define PVERE L"189" // wide environment string 8 | #define PVEREA "189" // ANSI environment string 9 | #define PVERB 1,8,9,0 // binary (resource) 10 | 11 | #ifdef _WIN64 12 | # define BITS L"64" 13 | # define BITSA "64" 14 | #else 15 | # define BITS L"32" 16 | # define BITSA "32" 17 | #endif 18 | 19 | #define ANSIDLL L"ANSI" BITS L".dll" 20 | -------------------------------------------------------------------------------- /G1.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal 2 | 3 | ::Extract the current code page. Hopefully this method will work with other 4 | ::languages. CHCP outputs: "Active code page: #". Take the last five 5 | ::characters (the longest code) and delete up to and including the space. 6 | for /f "delims=" %%j in ('chcp') do set CP=%%j 7 | set CP=%CP:~-5% 8 | set CP=%CP:* =% 9 | 10 | x86\ansicon -e The DEC Special Graphics Character Set according to code page %CP%:^ 11 | 12 | ^ 13 | 14 | _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~^ 15 | 16 | ^ 17 | 18 |  _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~ 19 | -------------------------------------------------------------------------------- /ansi.rc: -------------------------------------------------------------------------------- 1 | /* 2 | ansi.rc - Version resource for ANSI{32,64}.dll. 3 | 4 | Jason Hood, 11 November, 2009. 5 | */ 6 | 7 | #include 8 | #include "version.h" 9 | 10 | VS_VERSION_INFO VERSIONINFO 11 | FILEVERSION PVERB 12 | PRODUCTVERSION PVERB 13 | FILEOS VOS_NT 14 | FILETYPE VFT_DLL 15 | { 16 | BLOCK "StringFileInfo" 17 | { 18 | BLOCK "040904B0" 19 | { 20 | VALUE "Comments", "http://ansicon.adoxa.vze.com/" 21 | VALUE "CompanyName", "Jason Hood" 22 | VALUE "FileDescription", "ANSI Console" 23 | VALUE "FileVersion", PVERSA 24 | VALUE "InternalName", "ANSI" BITS 25 | VALUE "LegalCopyright", "Freeware" 26 | VALUE "OriginalFilename", ANSIDLL 27 | VALUE "ProductName", "ANSICON" 28 | VALUE "ProductVersion", PVERSA 29 | } 30 | } 31 | 32 | BLOCK "VarFileInfo" 33 | { 34 | VALUE "Translation", 0x0409, 0x04B0 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2005-2019 Jason Hood 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the author be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | Jason Hood 20 | jadoxa@yahoo.com.au 21 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ANSICON [![Latest release](http://img.shields.io/github/release/adoxa/ansicon.svg)](https://github.com/adoxa/ansicon/releases) 2 | 3 | ANSICON provides ANSI escape sequences for Windows console programs. It 4 | provides much the same functionality as `ANSI.SYS` does for MS-DOS. 5 | 6 | ## Requirements 7 | 8 | * 32-bit: Windows 2000 Professional and later (it won't work with NT or 9X). 9 | * 64-bit: AMD64 (it won't work with IA64). 10 | 11 | ## How it Works 12 | 13 | ANSICON *injects* a DLL into a process, *hooking* its functions. 14 | 15 | ### Injection 16 | 17 | One of three methods is used to inject the DLL. 18 | 19 | * `LoadLibrary` via `CreateRemoteThread` for a running process. 20 | 21 | * `LdrLoadDll` via `CreateRemoteThread` for a 64-bit .NET AnyCPU process. 22 | 23 | * Adding the DLL directly to the import table, otherwise. 24 | 25 | ### Hooking 26 | 27 | Hooking is achieved by modifying import addresses, or the return value of 28 | `GetProcAddress`. 29 | -------------------------------------------------------------------------------- /ansicon.rc: -------------------------------------------------------------------------------- 1 | /* 2 | ansicon.rc - Version resource for ansicon.exe. 3 | 4 | Jason Hood, 11 November, 2009. 5 | */ 6 | 7 | #include 8 | #include "version.h" 9 | 10 | VS_VERSION_INFO VERSIONINFO 11 | FILEVERSION PVERB 12 | PRODUCTVERSION PVERB 13 | FILEOS VOS_NT 14 | FILETYPE VFT_APP 15 | { 16 | BLOCK "StringFileInfo" 17 | { 18 | BLOCK "040904B0" 19 | { 20 | VALUE "Comments", "http://ansicon.adoxa.vze.com/" 21 | VALUE "CompanyName", "Jason Hood" 22 | VALUE "FileDescription", "ANSI Console" 23 | VALUE "FileVersion", PVERSA 24 | VALUE "InternalName", "ansicon" 25 | VALUE "LegalCopyright", "Freeware" 26 | VALUE "OriginalFilename", "ansicon.exe" 27 | VALUE "ProductName", "ANSICON" 28 | VALUE "ProductVersion", PVERSA 29 | } 30 | } 31 | 32 | BLOCK "VarFileInfo" 33 | { 34 | VALUE "Translation", 0x0409, 0x04B0 35 | } 36 | } 37 | 38 | // Add a manifest for the 32-bit version, to prevent registry redirection when 39 | // trying to use HKLM. 40 | #ifndef _WIN64 41 | 1 24 42 | { 43 | "\ 44 | \ 45 | \ 46 | \ 47 | \ 48 | \ 49 | \ 50 | \ 51 | \ 52 | " 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /procrva.c: -------------------------------------------------------------------------------- 1 | /* 2 | Get the RVA of a function directly from a module. This allows 64-bit code to 3 | work with 32-bit DLLs, and eliminates (or at least reduces) the possibility 4 | of the function already being hooked. 5 | */ 6 | 7 | #include "ansicon.h" 8 | 9 | static PIMAGE_DOS_HEADER pDosHeader; 10 | 11 | 12 | #ifdef _WIN64 13 | DWORD GetProcRVA( LPCTSTR module, LPCSTR func, int bits ) 14 | #else 15 | DWORD GetProcRVA( LPCTSTR module, LPCSTR func ) 16 | #endif 17 | { 18 | HMODULE hMod; 19 | TCHAR buf[MAX_PATH]; 20 | UINT len; 21 | PIMAGE_NT_HEADERS pNTHeader; 22 | PIMAGE_EXPORT_DIRECTORY pExportDir; 23 | PDWORD fun_table, name_table; 24 | PWORD ord_table; 25 | DWORD rva; 26 | int lo, mid, hi, cmp; 27 | 28 | #ifdef _WIN64 29 | if (bits == 32) 30 | len = GetSystemWow64Directory( buf, MAX_PATH ); 31 | else 32 | #endif 33 | len = GetSystemDirectory( buf, MAX_PATH ); 34 | buf[len++] = '\\'; 35 | lstrcpy( buf + len, module ); 36 | hMod = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE ); 37 | if (hMod == NULL) 38 | { 39 | #ifdef _WIN64 40 | DEBUGSTR( 1, "Unable to load %u-bit %S (%u)!", 41 | bits, module, GetLastError() ); 42 | #else 43 | DEBUGSTR( 1, "Unable to load %S (%u)!", module, GetLastError() ); 44 | #endif 45 | return 0; 46 | } 47 | // The handle uses low bits as flags, so strip 'em off. 48 | pDosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)hMod & ~0xFFFF); 49 | pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); 50 | #ifdef _WIN64 51 | if (bits == 32) 52 | pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, 53 | ((PIMAGE_NT_HEADERS32)pNTHeader)->EXPORTDIR.VirtualAddress ); 54 | else 55 | #endif 56 | pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, 57 | pNTHeader->EXPORTDIR.VirtualAddress ); 58 | fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions ); 59 | name_table = MakeVA( PDWORD, pExportDir->AddressOfNames ); 60 | ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals ); 61 | 62 | rva = 0; 63 | lo = 0; 64 | hi = pExportDir->NumberOfNames - 1; 65 | while (lo <= hi) 66 | { 67 | mid = (lo + hi) / 2; 68 | cmp = strcmp( func, MakeVA( LPCSTR, name_table[mid] ) ); 69 | if (cmp == 0) 70 | { 71 | rva = fun_table[ord_table[mid]]; 72 | break; 73 | } 74 | if (cmp < 0) 75 | hi = mid - 1; 76 | else 77 | lo = mid + 1; 78 | } 79 | if (rva == 0) 80 | { 81 | #ifdef _WIN64 82 | DEBUGSTR( 1, "Could not find %u-bit %s!", bits, func ); 83 | #else 84 | DEBUGSTR( 1, "Could not find %s!", func ); 85 | #endif 86 | } 87 | FreeLibrary( hMod ); 88 | return rva; 89 | } 90 | -------------------------------------------------------------------------------- /palette.h: -------------------------------------------------------------------------------- 1 | // Legacy console colors for XP (later systems get the actual palette). 2 | 3 | static const COLORREF legacy_palette[] = 4 | { 5 | 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0, 6 | 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF, 7 | }; 8 | 9 | // This is the Windows (10.0.15063) version of the xterm 256-color palette. 10 | 11 | static const COLORREF xterm_palette[] = 12 | { 13 | // 16 system colors left out. 14 | 15 | // RGB 6x6x6 color cube. 16 | 0x000000, 0x5F0000, 0x870000, 0xAF0000, 0xD70000, 0xFF0000, 17 | 0x005F00, 0x5F5F00, 0x875F00, 0xAF5F00, 0xD75F00, 0xFF5F00, 18 | 0x008700, 0x5F8700, 0x878700, 0xAF8700, 0xD78700, 0xFF8700, 19 | 0x00AF00, 0x5FAF00, 0x87AF00, 0xAFAF00, 0xD7AF00, 0xFFAF00, 20 | 0x00D700, 0x5FD700, 0x87D700, 0xAFD700, 0xD7D700, 0xFFD700, 21 | 0x00FF00, 0x5FFF00, 0x87FF00, 0xAFFF00, 0xD7FF00, 0xFFFF00, 22 | 0x00005F, 0x5F005F, 0x87005F, 0xAF005F, 0xD7005F, 0xFF005F, 23 | 0x005F5F, 0x5F5F5F, 0x875F5F, 0xAF5F5F, 0xD75F5F, 0xFF5F5F, 24 | 0x00875F, 0x5F875F, 0x87875F, 0xAF875F, 0xD7875F, 0xFF875F, 25 | 0x00AF5F, 0x5FAF5F, 0x87AF5F, 0xAFAF5F, 0xD7AF5F, 0xFFAF5F, 26 | 0x00D75F, 0x5FD75F, 0x87D75F, 0xAFD75F, 0xD7D75F, 0xFFD75F, 27 | 0x00FF5F, 0x5FFF5F, 0x87FF5F, 0xAFFF5F, 0xD7FF5F, 0xFFFF5F, 28 | 0x000087, 0x5F0087, 0x870087, 0xAF0087, 0xD70087, 0xFF0087, 29 | 0x005F87, 0x5F5F87, 0x875F87, 0xAF5F87, 0xD75F87, 0xFF5F87, 30 | 0x008787, 0x5F8787, 0x878787, 0xAF8787, 0xD78787, 0xFF8787, 31 | 0x00AF87, 0x5FAF87, 0x87AF87, 0xAFAF87, 0xD7AF87, 0xFFAF87, 32 | 0x00D787, 0x5FD787, 0x87D787, 0xAFD787, 0xD7D787, 0xFFD787, 33 | 0x00FF87, 0x5FFF87, 0x87FF87, 0xAFFF87, 0xD7FF87, 0xFFFF87, 34 | 0x0000AF, 0x5F00AF, 0x8700AF, 0xAF00AF, 0xD700AF, 0xFF00AF, 35 | 0x005FAF, 0x5F5FAF, 0x875FAF, 0xAF5FAF, 0xD75FAF, 0xFF5FAF, 36 | 0x0087AF, 0x5F87AF, 0x8787AF, 0xAF87AF, 0xD787AF, 0xFF87AF, 37 | 0x00AFAF, 0x5FAFAF, 0x87AFAF, 0xAFAFAF, 0xD7AFAF, 0xFFAFAF, 38 | 0x00D7AF, 0x5FD7AF, 0x87D7AF, 0xAFD7AF, 0xD7D7AF, 0xFFD7AF, 39 | 0x00FFAF, 0x5FFFAF, 0x87FFAF, 0xAFFFAF, 0xD7FFAF, 0xFFFFAF, 40 | 0x0000D7, 0x5F00D7, 0x8700D7, 0xAF00D7, 0xD700D7, 0xFF00D7, 41 | 0x005FD7, 0x5F5FD7, 0x875FD7, 0xAF5FD7, 0xD75FD7, 0xFF5FD7, 42 | 0x0087D7, 0x5F87D7, 0x8787D7, 0xAF87D7, 0xD787D7, 0xFF87D7, 43 | 0x00AFDF, 0x5FAFDF, 0x87AFDF, 0xAFAFDF, 0xD7AFDF, 0xFFAFDF, // xterm uses 44 | 0x00D7DF, 0x5FD7DF, 0x87D7DF, 0xAFD7DF, 0xD7D7DF, 0xFFD7DF, // R = 0xD7 45 | 0x00FFDF, 0x5FFFDF, 0x87FFDF, 0xAFFFDF, 0xD7FFDF, 0xFFFFDF, // here 46 | 0x0000FF, 0x5F00FF, 0x8700FF, 0xAF00FF, 0xD700FF, 0xFF00FF, 47 | 0x005FFF, 0x5F5FFF, 0x875FFF, 0xAF5FFF, 0xD75FFF, 0xFF5FFF, 48 | 0x0087FF, 0x5F87FF, 0x8787FF, 0xAF87FF, 0xD787FF, 0xFF87FF, 49 | 0x00AFFF, 0x5FAFFF, 0x87AFFF, 0xAFAFFF, 0xD7AFFF, 0xFFAFFF, 50 | 0x00D7FF, 0x5FD7FF, 0x87D7FF, 0xAFD7FF, 0xD7D7FF, 0xFFD7FF, 51 | 0x00FFFF, 0x5FFFFF, 0x87FFFF, 0xAFFFFF, 0xD7FFFF, 0xFFFFFF, 52 | 53 | // Grayscale, without black or white. 54 | 0x080808, 0x121212, 0x1C1C1C, 0x262626, 0x303030, 0x3A3A3A, 55 | 0x444444, 0x4E4E4E, 0x585858, 0x626262, 0x6C6C6C, 0x767676, 56 | 0x808080, 0x8A8A8A, 0x949494, 0x9E9E9E, 0xA8A8A8, 0xB2B2B2, 57 | 0xBCBCBC, 0xC6C6C6, 0xD0D0D0, 0xDADADA, 0xE4E4E4, 0xEEEEEE, 58 | }; 59 | -------------------------------------------------------------------------------- /makefile.gcc: -------------------------------------------------------------------------------- 1 | # MinGW/MinGW-w64 makefile for ANSICON. 2 | # Jason Hood, 11 March, 2006. Updated 20 June, 2009. 3 | # 4 | # 19 November, 2010: 5 | # explicitly use 64-bit flags, in case the compiler isn't. 6 | # 7 | # 13 December, 2011: 8 | # use CMD for file operations, not programs from fileutils. 9 | # 10 | # 23 November, 2012: 11 | # set the base address of the DLLs to AC0000[00] (AnsiCon). 12 | # 13 | # 17 & 18 July, 2013: 14 | # work with 32-bit only, 64-bit only or multilib compilers; 15 | # hide the commands (use V=1 to show them). 16 | # 17 | # 11 May, 2018: 18 | # update for the 1.84 changes. 19 | # 20 | # Tested with: 21 | # * MinGW/gcc 6.3.0; 22 | # * tdm-gcc-5.1.0-3; 23 | # * tdm64-gcc-5.1.0-2; 24 | # * MSYS2/gcc 7.3.0. 25 | 26 | CC ?= gcc 27 | ifeq ($(origin CC),default) 28 | ifeq ($(CC),cc) 29 | CC = gcc 30 | endif 31 | endif 32 | WINDRES ?= windres 33 | CFLAGS = -O2 -Wall -Wno-multichar 34 | 35 | # Identify ansicon.exe using "ANSI" as a version number. 36 | IVER = -Wl,--major-image-version,20033,--minor-image-version,18771 37 | 38 | #ARCH = 32 39 | #ARCH = 64 40 | #ARCH = multi 41 | 42 | ifndef ARCH 43 | # Use the machine to distinguish between MinGW and MinGW-w64. 44 | ifneq (,$(findstring i686-w64,$(shell $(CC) -dumpmachine))) 45 | ARCH = 32 46 | else 47 | ifeq (,$(findstring 64,$(shell $(CC) -dumpmachine))) 48 | ARCH = 32 49 | else 50 | # It's 64-bit, if it's multi the lib name will be different. 51 | ifeq ($(shell $(CC) -m32 -print-libgcc-file-name),$(shell $(CC) -m64 -print-libgcc-file-name)) 52 | ARCH = 64 53 | else 54 | ARCH = multi 55 | endif 56 | endif 57 | endif 58 | endif 59 | 60 | X86OBJS = x86/injdll.o x86/procrva.o x86/proctype.o x86/util.o 61 | X64OBJS = x64/injdll.o x64/procrva.o x64/proctype.o x64/util.o 62 | X6432OBJS = x86/injdll.o x86/procrva.o x64/proctype32.o x86/util.o 63 | 64 | # Determine the appropriate separator to run multiple commands - ";" for sh.exe 65 | # and "&" for CMD.EXE. $(SHELL) is initially defined to "sh.exe" - if it 66 | # actually exists, it becomes the full path. 67 | ifneq ($(wildcard $(SHELL)),) 68 | SEP = ; 69 | else 70 | SEP = & 71 | endif 72 | 73 | V ?= 0 74 | ifeq ($(V),0) 75 | CCmsg = @echo $<$(SEP) 76 | RCmsg = $(CCmsg) 77 | LDmsg = @echo $@$(SEP) 78 | endif 79 | 80 | x86/%.o: %.c ansicon.h 81 | $(CCmsg)$(CC) -m32 -c $(CFLAGS) $< -o $@ 82 | 83 | x86/%v.o: %.rc version.h 84 | $(RCmsg)$(WINDRES) -U _WIN64 -F pe-i386 $< $@ 85 | 86 | x64/%.o: %.c ansicon.h 87 | $(CCmsg)$(CC) -m64 -g -c $(CFLAGS) $< -o $@ 88 | 89 | x64/%v.o: %.rc version.h 90 | $(RCmsg)$(WINDRES) -F pe-x86-64 $< $@ 91 | 92 | x64/%32.o: %.c 93 | $(CCmsg)$(CC) -m32 -DW32ON64 $(CFLAGS) $< -c -o $@ 94 | 95 | 96 | ifeq ($(ARCH),multi) 97 | all: ansicon32 ansicon64 98 | else 99 | all: ansicon$(ARCH) 100 | endif 101 | 102 | ansicon32: x86 x86/ANSI32.dll x86/ansicon.exe x64 x64/ANSI32.dll 103 | 104 | ansicon64: x64 x64/ANSI64.dll x64/ansicon.exe 105 | 106 | x86: 107 | mkdir x86 108 | 109 | x86/ansicon.exe: x86/ansicon.o x86/ANSI32.dll x86/ansiconv.o 110 | $(LDmsg)$(CC) -m32 $+ -s -o $@ $(IVER) 111 | 112 | x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o 113 | $(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll -nostdlib -lkernel32 -lntdll \ 114 | -Wl,-shared,--image-base,0xAC0000,-e,_DllMain@12 115 | 116 | x64: 117 | mkdir x64 118 | 119 | x64/ansicon.exe: x64/ansicon.o x64/ANSI64.dll x64/ansiconv.o 120 | $(LDmsg)$(CC) -m64 $+ -s -o $@ $(IVER) 121 | 122 | x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o 123 | $(LDmsg)$(CC) -m64 $+ -s -o $@ -mdll -nostdlib -lkernel32 -lntdll \ 124 | -Wl,-shared,--image-base,0xAC000000,-e,DllMain 125 | 126 | x64/ANSI32.dll: x64/ANSI32.o $(X6432OBJS) x86/ansiv.o 127 | $(LDmsg)$(CC) -m32 $+ -s -o $@ -mdll -nostdlib -lkernel32 -lntdll \ 128 | -Wl,-shared,--image-base,0xAC0000,-e,_DllMain@12,--large-address-aware 129 | 130 | x86/ansicon.o: version.h 131 | x86/ANSI.o: version.h 132 | x86/util.o: version.h 133 | x64/ansicon.o: version.h 134 | x64/ANSI.o: version.h 135 | x64/util.o: version.h 136 | 137 | # Need two commands, because if the directory doesn't exist, it won't delete 138 | # anything at all. 139 | clean: 140 | ifneq ($(wildcard $(SHELL)),) 141 | -rm -f x86/*.o 2>/dev/null 142 | -rm -f x64/*.o 2>/dev/null 143 | else 144 | -cmd /c "del x86\*.o 2>nul" 145 | -cmd /c "del x64\*.o 2>nul" 146 | endif 147 | -------------------------------------------------------------------------------- /makefile.vc: -------------------------------------------------------------------------------- 1 | # VC makefile for ANSICON. 2 | # Jason Hood, 15 November, 2010. 3 | # 4 | # Tested with: 5 | # * Visual Studio 6.0 (VC6); 6 | # * Visual C++ 2003 Toolkit (VC7); 7 | # * Platform SDK for Windows Server 2003 R2 (VC8 64-bit); 8 | # * Visual Studio 2008 Express SP1 (VC9); 9 | # * Visual Studio 2010 Professional (VC10). 10 | # 11 | # Note that the 64-bit version still requires a 32-bit compiler. 12 | # 13 | # 22 & 23 November, 2012: 14 | # determine if the PSDK is used automatically; 15 | # use AC0000[00] (AnsiCon) as the base address; 16 | # twiddle stuff around to support VC6 (with 2003 PSDK) for the 32-bit version; 17 | # determine BITS automatically. 18 | # 19 | # 18 July, 2013: 20 | # hide the commands (use V=1 to show them). 21 | # 22 | # 30 April, 8 to 11 May 2018: 23 | # use a batch rule (even if this project is too small to make a difference); 24 | # add /nologo to RFLAGS if rc supports it; 25 | # explicitly link the exe with MSVCRT.DLL. 26 | 27 | #BITS = 32 28 | #BITS = 64 29 | 30 | !IFNDEF BITS 31 | !IF "$(CPU)" == "AMD64" || "$(PLATFORM)" == "x64" || "$(PLATFORM)" == "X64" 32 | BITS = 64 33 | !ELSE 34 | BITS = 32 35 | !ENDIF 36 | !ENDIF 37 | 38 | !IF $(BITS) == 32 39 | DIR = x86 40 | !ELSE 41 | !IF $(BITS) == 64 42 | DIR = x64 43 | !ELSE 44 | !ERROR BITS should be defined to 32 or 64. 45 | !ENDIF 46 | !ENDIF 47 | 48 | # Disable security checks, but VC6 & 7 don't have /GS-. 49 | !IF "$(_NMAKE_VER)" != "7.00.8882" || $(BITS) == 64 50 | NOSECCHK = /GS- 51 | !ENDIF 52 | 53 | # 2008 (SDK v6) and earlier rc do not have /nologo. 54 | !IF [cmd /d /c exit $(_NMAKE_VER)] <= 9 55 | RFLAGS = 56 | !ENDIF 57 | 58 | # Link with MSVCRT.LIB by default. 59 | !IFNDEF SHARE 60 | SHARE = /MD 61 | CRT = $(DIR)\acrt.lib 62 | LINK = /entry:main /nod 63 | # The 2003 Toolkit has chkstk in libc, but the rest have it as itself. 64 | !IF "$(_NMAKE_VER)" == "7.00.8882" && $(BITS) == 32 && !DEFINED(MSVCDIR) 65 | LIBS = libc.lib 66 | !ELSE 67 | LIBS = chkstk.obj 68 | !ENDIF 69 | !ENDIF 70 | 71 | !IFNDEF RFLAGS 72 | RFLAGS = /nologo 73 | !ENDIF 74 | !IF $(BITS) == 64 75 | RFLAGS = $(RFLAGS) /D_WIN64 76 | !ENDIF 77 | CFLAGS = /nologo /W3 /O2 $(NOSECCHK) /D_CRT_SECURE_NO_WARNINGS 78 | LIBS = $(LIBS) kernel32.lib advapi32.lib 79 | 80 | # Identify ansicon.exe using "ANSI" as a version number. 81 | LINK = /link /version:20033.18771 $(LINK) /fixed 82 | 83 | X86OBJS = x86\injdll.obj x86\procrva.obj x86\proctype.obj x86\util.obj 84 | X64OBJS = x64\injdll.obj x64\procrva.obj x64\proctype.obj x64\util.obj 85 | X6432OBJS = x86\injdll.obj x86\procrva.obj x64\proctype32.obj x86\util.obj 86 | 87 | !IF !DEFINED(V) 88 | V = 0 89 | !ENDIF 90 | !IF $(V) == 0 91 | CCmsg = @ 92 | RCmsg = @echo $<& 93 | LDmsg = @echo $@& 94 | !ENDIF 95 | 96 | {}.c{$(DIR)}.obj:: 97 | $(CCmsg)$(CC) /c $(CFLAGS) /Fo$(DIR)\ $< 98 | 99 | {}.rc{$(DIR)}.res: 100 | $(RCmsg)$(RC) $(RFLAGS) /fo$@ $< 101 | 102 | all: ansicon$(BITS) 103 | 104 | ansicon32: x86 x86\ANSI32.dll x86\ansicon.exe x64 x64\ANSI32.dll 105 | 106 | ansicon64: x64 x64\ANSI64.dll x64\ansicon.exe 107 | 108 | x86: 109 | mkdir x86 110 | 111 | x86\ansicon.exe: x86\ansicon.obj x86\ansi32.lib $(CRT) x86\ansicon.res 112 | $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512 113 | !IF "$(_NMAKE_VER)" == "9.00.30729.01" 114 | @del $@.manifest 115 | !ENDIF 116 | 117 | x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res 118 | $(LDmsg)$(CC) /nologo /LD /Fe$@ $** kernel32.lib /link \ 119 | /base:0xAC0000 /entry:DllMain /filealign:512 120 | 121 | x64: 122 | mkdir x64 123 | 124 | x64\ansicon.exe: x64\ansicon.obj x64\ansi64.lib $(CRT) x64\ansicon.res 125 | $(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) 126 | 127 | x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res 128 | $(LDmsg)$(CC) /nologo /LD /Fe$@ $** kernel32.lib /link \ 129 | /base:0xAC000000 /entry:DllMain 130 | 131 | x64\ANSI32.dll: x64\ANSI32.obj $(X6432OBJS) x86\ansi.res 132 | $(LDmsg)$(CC) /nologo /LD /Fe$@ $** kernel32.lib /link \ 133 | /base:0xAC0000 /entry:DllMain /filealign:512 \ 134 | /largeaddressaware 135 | 136 | ansicon.c: ansicon.h version.h 137 | ansicon.rc: version.h 138 | ANSI.c: ansicon.h version.h 139 | ANSI.rc: version.h 140 | util.c: ansicon.h version.h 141 | injdll.c: ansicon.h 142 | proctype.c: ansicon.h 143 | procrva.c: ansicon.h 144 | 145 | $(DIR)\ansicon.obj: 146 | $(CCmsg)$(CC) /c $(CFLAGS) $(SHARE) /Fo$@ $? 147 | 148 | x64\ANSI32.obj: ANSI.c 149 | $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? 150 | 151 | x64\proctype32.obj: proctype.c 152 | $(CCmsg)$(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $? 153 | 154 | x86\acrt.lib: acrt.def 155 | $(LDmsg)link /lib /nologo /def:acrt.def /machine:ix86 /name:msvcrt /out:$@ 156 | 157 | x64\acrt.lib: acrt.def 158 | $(LDmsg)link /lib /nologo /def:acrt.def /machine:amd64 /name:msvcrt /out:$@ 159 | 160 | clean: 161 | -del $(DIR)\*.obj $(DIR)\*.res $(DIR)\*.lib $(DIR)\*.exp 162 | !IF $(BITS) == 32 163 | -del x64\ansi32.obj x64\proctype32.obj 164 | !ENDIF 165 | -------------------------------------------------------------------------------- /ansicon.h: -------------------------------------------------------------------------------- 1 | /* 2 | ansicon.h - Header file for common definitions. 3 | 4 | Jason Hood, 12 December, 2010 (originally injdll.h, 20 June, 2009). 5 | */ 6 | 7 | #ifndef ANSICON_H 8 | #define ANSICON_H 9 | 10 | #ifndef UNICODE 11 | # define UNICODE 12 | #endif 13 | 14 | #define WIN32_LEAN_AND_MEAN 15 | #ifdef _WIN64 16 | #define _WIN32_WINNT 0x0501 // at least XP required 17 | #else 18 | #define _WIN32_WINNT 0x0500 // at least Windows 2000 required 19 | #endif 20 | #define WINVER _WIN32_WINNT 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef INVALID_FILE_ATTRIBUTES 27 | #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 28 | #endif 29 | #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE 30 | #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20 31 | #endif 32 | #ifndef LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 33 | #define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x20 34 | #endif 35 | #ifndef TH32CS_SNAPMODULE32 36 | #define TH32CS_SNAPMODULE32 0x10 37 | #endif 38 | #if !defined(HandleToULong) && !defined(_WIN64) 39 | #define HandleToULong HandleToUlong 40 | #endif 41 | 42 | #ifndef __IMAGE_COR20_HEADER_DEFINED__ 43 | #define COMIMAGE_FLAGS_ILONLY 1 44 | #define COMIMAGE_FLAGS_32BITREQUIRED 2 45 | 46 | // CLR 2.0 header structure. 47 | typedef struct IMAGE_COR20_HEADER 48 | { 49 | DWORD cb; 50 | WORD MajorRuntimeVersion; 51 | WORD MinorRuntimeVersion; 52 | IMAGE_DATA_DIRECTORY MetaData; 53 | DWORD Flags; 54 | union { 55 | DWORD EntryPointToken; 56 | DWORD EntryPointRVA; 57 | } DUMMYUNIONNAME; 58 | IMAGE_DATA_DIRECTORY Resources; 59 | IMAGE_DATA_DIRECTORY StrongNameSignature; 60 | IMAGE_DATA_DIRECTORY CodeManagerTable; 61 | IMAGE_DATA_DIRECTORY VTableFixups; 62 | IMAGE_DATA_DIRECTORY ExportAddressTableJumps; 63 | IMAGE_DATA_DIRECTORY ManagedNativeHeader; 64 | } IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER; 65 | #endif 66 | 67 | #define lenof(array) (sizeof(array)/sizeof(*(array))) 68 | #define TSIZE(size) ((size) * sizeof(TCHAR)) 69 | #define PTRSZ sizeof(PVOID) 70 | 71 | // Macro for adding pointers/DWORDs together without C arithmetic interfering 72 | #define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset)) 73 | 74 | #define DATADIRS OptionalHeader.NumberOfRvaAndSizes 75 | #define EXPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] 76 | #define IMPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 77 | #define BOUNDDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] 78 | #define IATDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] 79 | #define COMDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] 80 | 81 | // Reduce the verbosity of some functions (assuming variable names). 82 | #define ReadProcVar(a, b) ReadProcMem( a, b, sizeof(*(b)) ) 83 | #define WriteProcVar(a, b) WriteProcMem( a, b, sizeof(*(b)) ) 84 | #define ReadProcMem(a, b, c) ReadProcessMemory( ppi->hProcess, a, b, c, NULL ) 85 | #define WriteProcMem(a, b, c) WriteProcessMemory( ppi->hProcess, a, b, c, NULL ) 86 | #define VirtProtVar(a, b) VirtualProtectEx( ppi->hProcess, a, sizeof(*(a)), b, &pr ) 87 | 88 | 89 | #ifdef PDATE // i.e. from ansicon.c 90 | #define EXTERN __declspec(dllimport) extern 91 | #else 92 | #define EXTERN __declspec(dllexport) extern 93 | #endif 94 | 95 | EXTERN BOOL IsConsoleHandle( HANDLE ); 96 | EXTERN int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* ); 97 | BOOL Wow64Process( HANDLE ); 98 | 99 | #ifdef _WIN64 100 | EXTERN 101 | #endif 102 | void InjectDLL( LPPROCESS_INFORMATION, PBYTE ); 103 | void RemoteLoad32( LPPROCESS_INFORMATION ); 104 | #ifdef _WIN64 105 | void InjectDLL32( LPPROCESS_INFORMATION, PBYTE ); 106 | EXTERN void RemoteLoad64( LPPROCESS_INFORMATION ); 107 | EXTERN DWORD GetProcRVA( LPCTSTR, LPCSTR, int ); 108 | #else 109 | EXTERN DWORD GetProcRVA( LPCTSTR, LPCSTR ); 110 | #endif 111 | 112 | extern HANDLE hHeap; 113 | 114 | EXTERN TCHAR prog_path[MAX_PATH]; 115 | extern LPTSTR prog; 116 | LPTSTR get_program_name( LPTSTR ); 117 | 118 | EXTERN TCHAR DllName[MAX_PATH]; 119 | EXTERN LPTSTR DllNameType; 120 | extern char ansi_dll[MAX_PATH]; 121 | extern DWORD ansi_len; 122 | extern char* ansi_bits; 123 | void set_ansi_dll( void ); 124 | DWORD get_os_version( void ); 125 | 126 | EXTERN int log_level; 127 | EXTERN void DEBUGSTR( int level, LPCSTR szFormat, ... ); 128 | 129 | // Replacements for C runtime functions. 130 | #ifdef _MSC_VER 131 | #undef RtlFillMemory 132 | #undef RtlMoveMemory 133 | #undef RtlZeroMemory 134 | void WINAPI RtlFillMemory( PVOID, SIZE_T, BYTE ); 135 | void WINAPI RtlMoveMemory( PVOID, const VOID*, SIZE_T ); 136 | void WINAPI RtlZeroMemory( PVOID, SIZE_T ); 137 | #endif 138 | 139 | #define arrcpy( dst, src ) RtlMoveMemory( dst, src, sizeof(dst) ) 140 | 141 | unsigned long ac_wcstoul( const wchar_t*, wchar_t**, int ); 142 | int ac_wtoi( const wchar_t* ); 143 | long ac_wcstol( const wchar_t*, wchar_t**, int ); 144 | wchar_t* ac_wcspbrk( const wchar_t*, const wchar_t* ); 145 | wchar_t* ac_wcsrchr( const wchar_t*, wchar_t ); 146 | int ac_strnicmp( const char*, const char*, size_t ); 147 | int ac_sprintf( char*, const char*, ... ); 148 | int ac_wprintf( wchar_t*, const char*, ... ); 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /proctype.c: -------------------------------------------------------------------------------- 1 | /* 2 | Test for a valid process (i386 for x86; that or AMD64 for x64). We can get 3 | that info from the image header, which means getting the process's base 4 | address (which we need anyway, to modify the imports). The simplest way to 5 | do that is to enumerate the pages, looking for an executable image. A .NET 6 | AnyCPU process has a 32-bit structure, but will load as 64-bit when possible. 7 | The 64-bit version (both DLLs) will say this is type 48 (halfway between 32 & 8 | 64); the 32-bit version will ignore it if run on a 64-bit OS. 9 | */ 10 | 11 | #include "ansicon.h" 12 | 13 | 14 | #if !defined(_WIN64) && !defined(W32ON64) 15 | static BOOL ProcessIs64( HANDLE hProcess ) 16 | { 17 | BOOL wow; 18 | 19 | typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)( HANDLE, PBOOL ); 20 | static LPFN_ISWOW64PROCESS fnIsWow64Process; 21 | 22 | if (fnIsWow64Process == INVALID_HANDLE_VALUE) 23 | return FALSE; 24 | 25 | if (fnIsWow64Process == NULL) 26 | { 27 | fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( 28 | GetModuleHandle( L"kernel32.dll" ), "IsWow64Process" ); 29 | if (fnIsWow64Process == NULL) 30 | { 31 | fnIsWow64Process = INVALID_HANDLE_VALUE; 32 | return FALSE; 33 | } 34 | } 35 | 36 | // If IsWow64Process fails, say it is 64, since injection probably wouldn't 37 | // work, either. 38 | return !(fnIsWow64Process( hProcess, &wow ) && wow); 39 | } 40 | #endif 41 | 42 | 43 | int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui ) 44 | { 45 | PBYTE ptr; 46 | MEMORY_BASIC_INFORMATION minfo; 47 | IMAGE_DOS_HEADER dos_header; 48 | IMAGE_NT_HEADERS nt_header; 49 | PBYTE dummy_base; 50 | BOOL dummy_gui; 51 | int log; 52 | 53 | // Don't log if we're only getting one value, as it's already been logged. 54 | log = 1; 55 | if (pBase == NULL) 56 | { 57 | pBase = &dummy_base; 58 | log = 128; 59 | } 60 | if (gui == NULL) 61 | { 62 | gui = &dummy_gui; 63 | log = 128; 64 | } 65 | 66 | *pBase = NULL; 67 | *gui = FALSE; 68 | 69 | for (ptr = NULL; 70 | VirtualQueryEx( ppi->hProcess, ptr, &minfo, sizeof(minfo) ); 71 | ptr += minfo.RegionSize) 72 | { 73 | if (minfo.BaseAddress == minfo.AllocationBase 74 | && ReadProcVar( minfo.BaseAddress, &dos_header ) 75 | && dos_header.e_magic == IMAGE_DOS_SIGNATURE 76 | && ReadProcVar( (PBYTE)minfo.BaseAddress + dos_header.e_lfanew, 77 | &nt_header ) 78 | && nt_header.Signature == IMAGE_NT_SIGNATURE 79 | && !(nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL)) 80 | { 81 | // Don't load into ansicon.exe, it's already imported. 82 | if (nt_header.OptionalHeader.MajorImageVersion == 20033 && // 'AN' 83 | nt_header.OptionalHeader.MinorImageVersion == 18771) // 'SI' 84 | return -1; 85 | 86 | *pBase = minfo.BaseAddress; 87 | if (nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) 88 | *gui = TRUE; 89 | if (*gui || 90 | nt_header.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI) 91 | { 92 | if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) 93 | { 94 | PIMAGE_NT_HEADERS32 pNTHeader = (PIMAGE_NT_HEADERS32)&nt_header; 95 | if (pNTHeader->DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR && 96 | pNTHeader->COMDIR.VirtualAddress != 0) 97 | { 98 | IMAGE_COR20_HEADER ComHeader, *pComHeader; 99 | pComHeader = (PIMAGE_COR20_HEADER)((PBYTE)minfo.BaseAddress 100 | + pNTHeader->COMDIR.VirtualAddress); 101 | ReadProcVar( pComHeader, &ComHeader ); 102 | if ((ComHeader.Flags & COMIMAGE_FLAGS_ILONLY) && 103 | !(ComHeader.Flags & COMIMAGE_FLAGS_32BITREQUIRED)) 104 | { 105 | DEBUGSTR( log, " AnyCPU %s (base = %q)", 106 | (*gui) ? "GUI" : "console", minfo.BaseAddress ); 107 | #if defined(_WIN64) || defined(W32ON64) 108 | return 48; 109 | #else 110 | if (ProcessIs64( ppi->hProcess )) 111 | { 112 | DEBUGSTR( log, " Unsupported (use x64\\ansicon)" ); 113 | return 0; 114 | } 115 | return 32; 116 | #endif 117 | } 118 | } 119 | DEBUGSTR( log, " 32-bit %s (base = %q)", 120 | (*gui) ? "GUI" : "console", minfo.BaseAddress ); 121 | return 32; 122 | } 123 | if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) 124 | { 125 | #ifdef _WIN64 126 | DEBUGSTR( log, " 64-bit %s (base = %p)", 127 | (*gui) ? "GUI" : "console", minfo.BaseAddress ); 128 | return 64; 129 | #else 130 | DEBUGSTR( log, " 64-bit %s (base = %P)", 131 | (*gui) ? "GUI" : "console", minfo.BaseAddress ); 132 | #if defined(W32ON64) 133 | return 64; 134 | #else 135 | DEBUGSTR( log, " Unsupported (use x64\\ansicon)" ); 136 | return 0; 137 | #endif 138 | #endif 139 | } 140 | DEBUGSTR( log, " Ignoring unsupported machine (0x%X)", 141 | nt_header.FileHeader.Machine ); 142 | } 143 | else 144 | { 145 | DEBUGSTR( log, " Ignoring unsupported subsystem (%u)", 146 | nt_header.OptionalHeader.Subsystem ); 147 | } 148 | return 0; 149 | } 150 | #ifndef _WIN64 151 | // If a 32-bit process loads a 64-bit one, we may miss the base 152 | // address. If the pointer overflows, assume 64-bit. 153 | if (((DWORD)ptr >> 12) + ((DWORD)minfo.RegionSize >> 12) >= 0x100000) 154 | { 155 | #ifdef W32ON64 156 | DEBUGSTR( log, " Pointer overflow: assuming 64-bit" ); 157 | return 64; 158 | #else 159 | DEBUGSTR( log, " Ignoring apparent 64-bit process (use x64\\ansicon)" ); 160 | return 0; 161 | #endif 162 | } 163 | #endif 164 | } 165 | 166 | DEBUGSTR( log, " Ignoring non-Windows process" ); 167 | return 0; 168 | } 169 | -------------------------------------------------------------------------------- /sequences.txt: -------------------------------------------------------------------------------- 1 | 2 | ANSICON 3 | Version 1.81 4 | 5 | This is a complete list of the ANSI/VT escape sequences recognised by ANSICON, 6 | roughly ordered by function. The initial escape character is assumed. The 7 | display consists of the buffer width and window height; add '+' before the 8 | final character to use the buffer height (e.g. "[2J" will erase the window, 9 | whilst "[2+J" will erase the buffer). BEL will also be recognised, playing 10 | the Windows default beep (but only if it's not already playing). 11 | 12 | 13 | [m restore default color (and intensity) 14 | [0m as above 15 | [...m set attributes (any of these numbers, separated by semicolons): 16 | 0 all attributes off 17 | 1 bold (foreground is intense) 18 | 4 underline (background is intense) 19 | 5 blink (background is intense) 20 | 7 reverse video 21 | 8 concealed (foreground becomes background) 22 | 22 bold off (foreground is not intense) 23 | 24 underline off (background is not intense) 24 | 25 blink off (background is not intense) 25 | 27 normal video 26 | 28 concealed off 27 | 30 foreground black 28 | 31 foreground red 29 | 32 foreground green 30 | 33 foreground yellow 31 | 34 foreground blue 32 | 35 foreground magenta 33 | 36 foreground cyan 34 | 37 foreground white 35 | 38;2;# foreground based on index (0-255) 36 | 38;5;#;#;# foreground based on RGB 37 | 39 default foreground (using current intensity) 38 | 40 background black 39 | 41 background red 40 | 42 background green 41 | 43 background yellow 42 | 44 background blue 43 | 45 background magenta 44 | 46 background cyan 45 | 47 background white 46 | 48;2;# background based on index (0-255) 47 | 48;5;#;#;# background based on RGB 48 | 49 default background (using current intensity) 49 | 90 foreground bright black 50 | 91 foreground bright red 51 | 92 foreground bright green 52 | 93 foreground bright yellow 53 | 94 foreground bright blue 54 | 95 foreground bright magenta 55 | 96 foreground bright cyan 56 | 97 foreground bright white 57 | 100 background bright black 58 | 101 background bright red 59 | 102 background bright green 60 | 103 background bright yellow 61 | 104 background bright blue 62 | 105 background bright magenta 63 | 106 background bright cyan 64 | 107 background bright white 65 | 66 | Index is 0-7 for the normal colors and 8-15 for the bright; 16-231 67 | are a 6x6x6 color cube; and 232-255 are a grayscale ramp (without 68 | black or white). Indices 16-255 and RGB colors will find the nearest 69 | color from the first 16. 70 | 71 | [J erase from cursor to the end of display 72 | [0J as above 73 | [1J erase from the start of diplay to cursor (inclusive) 74 | [2J erase display and move cursor to the top-left 75 | 76 | [K erase from cursor to the end of line 77 | [0K as above 78 | [1K erase from the start of line to cursor (inclusive) 79 | [2K erase line 80 | 81 | [X erase one character 82 | [#X erase # characters 83 | 84 | [L insert one blank line 85 | [#L insert # blank lines 86 | 87 | [M delete one line 88 | [#M delete # lines 89 | 90 | [P delete one character 91 | [#P delete # characters 92 | 93 | [@ insert one blank character 94 | [#@ insert # blank characters 95 | 96 | [b repeat the previous character 97 | [#b repeat the previous character # times 98 | 99 | D move cursor down one line (scroll if necessary; always uses buffer) 100 | E same as LF 101 | M move cursor up one line (scroll if necessary; always uses buffer) 102 | 103 | [A move cursor up one line 104 | [#A move cursor up # lines 105 | [B move cursor down one line 106 | [#B move cursor down # lines 107 | [C move cursor right one character 108 | [#C move cursor right # characters 109 | [D move cursor left one character 110 | [#D move cursor left # characters 111 | 112 | H set tab stop 113 | [g remove tab stop at cursor 114 | [0g as above 115 | [3g remove all tab stops 116 | [8g restore console tab handling (ANSICON extension) 117 | [?5W set tab stops every 8 columns 118 | [?5;#W set tab stops every # columns (ANSICON extension) 119 | [I move cursor forward one tab 120 | [#I move cursor forward # tabs 121 | [Z move cursor back one tab 122 | [#Z move cursor back # tabs 123 | 124 | [k move cursor up one line 125 | [#k move cursor up # lines 126 | [e move cursor down one line 127 | [#e move cursor down # lines 128 | [a move cursor right one character 129 | [#a move cursor right # characters 130 | [j move cursor left one character 131 | [#j move cursor left # characters 132 | 133 | [E move cursor down one line and to first column 134 | [#E move cursor down # lines and to first column 135 | [F move cursor up one line and to first column 136 | [#F move cursor up # lines and to first column 137 | 138 | [G move cursor to first column 139 | [#G move cursor to column # 140 | 141 | [` move cursor to first column 142 | [#` move cursor to column # 143 | 144 | [d move cursor to first line 145 | [#d move cursor to line # 146 | 147 | [H move cursor to top-left 148 | [#H move cursor to line # and first column 149 | [#;#H move cursor to line #, column # 150 | 151 | [f move cursor to top-left 152 | [#f move cursor to line # and first column 153 | [#;#f move cursor to line #, column # 154 | 155 | [s save cursor position (buffer only) 156 | [u move cursor to saved position (or top-left, if nothing was saved) 157 | 7 save cursor position (buffer only), attributes and G0 character set 158 | 8 restore above (if nothing was saved only moves cursor to top-left) 159 | 160 | [+r remove top and bottom margins 161 | [r set top and bottom margins to the window 162 | [#r set top margin to line #, bottom margin to the window 163 | [#;#r set top margin to line #, bottom margin to line # 164 | 165 | [S scroll up (pan down) one line 166 | [#S scroll up (pan down) # lines 167 | [T scroll down (pan up) one line 168 | [#T scroll down (pan up) # lines 169 | 170 | (0 select the DEC Special Graphics Character Set 171 | (B select ASCII 172 | 173 | [1+h flush immediately on write 174 | [1+l flush only when necessary 175 | [3h display control characters (LF is also performed) 176 | [3l perform control functions (the only such recognised during above) 177 | [4h insert characters 178 | [4l replace characters 179 | [?3h set 132 columns 180 | [?3l restore original columns 181 | [?6h set origin to top margin 182 | [?6l set origin to top line 183 | [?7h wrap lines at screen edge 184 | [?7l don't wrap lines at screen edge 185 | [?25h show cursor 186 | [?25l hide cursor 187 | [?95h don't clear screen when changing columns 188 | [?95l clear screen when changing columns 189 | 190 | [!p soft reset: 191 | show cursor 192 | perform control functions 193 | replace characters 194 | set origin to top line 195 | wrap lines at screen edge 196 | select ASCII 197 | remove margins 198 | default SGR 199 | set the saved cursor to the top-left 200 | c hard reset: 201 | as above 202 | restore console tab handling 203 | restore the entire palette 204 | restore original columns 205 | clear screen when changing columns 206 | clear the buffer 207 | home the cursor 208 | 209 | [c sends "\e[?62;1c" to console input (where \e is escape) 210 | [0c as above 211 | [5n sends "\e[0n" to console input 212 | [6n sends "\e[#;#R" (line & column) to console input 213 | [21t sends "\e]lTitle\e\" (the console's window title) to console input 214 | ]0;TitleST 215 | ]2;TitleST 216 | sets the console title to "Title"; ST (string terminator) is either 217 | character 7 (BEL) or escape and backslash 218 | ]4;#;spec,spec...;#...ST 219 | set or query the palette: 220 | # is the ANSI index (0-7, or 8-15 for bright) 221 | spec is: 222 | ? send the current value to console input 223 | * send the current and all subsequent values 224 | (up to 15 if index is less than that) 225 | #RGB set the color (hexadecimal) 226 | #RRGGBB set the color (hexadecimal) 227 | R,G,B set the color (decimal) 228 | rgb:RR/GG/BB set the color (hexadecimal) 229 | rgb:RRRR/GGGG/BBBB set the color (hexadecimal, high byte) 230 | ]104ST restore the entire palette 231 | ]104;#...ST 232 | restore the color of each index 233 | 234 | [#;#;#...,~ 235 | play sound (beep): 236 | the first value is volume: 0 is silence, anything else is ignored 237 | the second value is duration: up to and including 48 is in 1/32 of a 238 | second; anything else is milliseconds (maximum of 8000) 239 | the remaining values (if any; 14 allowed) are notes or frequencies: 240 | Value Note Freq Value Note Freq Value Note Freq 241 | 0 silence 0 9 G#5/Ab5 830 18 F6 1396 242 | 1 C5 524 10 A5 880 19 F#6/Gb6 1480 243 | 2 C#5/Db5 554 11 A#5/Bb5 932 20 G6 1568 244 | 3 D5 588 12 B5 988 21 G#6/Ab6 1662 245 | 4 D#5/Eb5 622 13 C6 1046 22 A6 1760 246 | 5 E5 660 14 C#6/Db6 1108 23 A#6/Bb6 1864 247 | 6 F5 698 15 D6 1174 24 B6 1976 248 | 7 F#5/Gb5 740 16 D#6/Eb6 1244 25 C7 2094 249 | 8 G5 784 17 E6 1318 250 | 251 | P...ST ignored (Device Control String) 252 | X...ST ignored (Start Of String) 253 | ^...ST ignored (Privacy Message) 254 | _...ST ignored (Application Program Command) 255 | 256 | Any of space and !"#$%&'()*+,-./, possibly repeated, followed by anything else, 257 | will be ignored. 258 | -------------------------------------------------------------------------------- /injdll.c: -------------------------------------------------------------------------------- 1 | /* 2 | Inject the DLL into the target process by modifying its import descriptor 3 | table. The target process must have been created suspended. However, for a 4 | 64-bit system with a .NET AnyCPU process, inject via LdrLoadDll in ntdll.dll 5 | and CreateRemoteThread (since AnyCPU is stored as i386, but loads as AMD64, 6 | preventing imports from working). 7 | */ 8 | 9 | #include "ansicon.h" 10 | 11 | 12 | // Search for a suitable free area after the main image (32-bit code could 13 | // really go anywhere, but let's keep it relatively local.) 14 | static PVOID FindMem( HANDLE hProcess, PBYTE base, DWORD len ) 15 | { 16 | MEMORY_BASIC_INFORMATION minfo; 17 | PBYTE ptr; 18 | PVOID mem; 19 | 20 | for (ptr = base; 21 | VirtualQueryEx( hProcess, ptr, &minfo, sizeof(minfo) ); 22 | ptr += minfo.RegionSize) 23 | { 24 | if ((minfo.State & MEM_FREE) && minfo.RegionSize >= len) 25 | { 26 | #ifdef _WIN64 27 | if ((PBYTE)minfo.BaseAddress - base > 0xFFFFffff - len) 28 | return NULL; 29 | #endif 30 | // Round up to the allocation granularity, presumed to be 64Ki. 31 | mem = VirtualAllocEx( hProcess, (PVOID) 32 | (((DWORD_PTR)minfo.BaseAddress + 0xFFFF) & ~0xFFFF), 33 | len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); 34 | if (mem != NULL) 35 | return mem; 36 | } 37 | } 38 | return NULL; 39 | } 40 | 41 | 42 | // Count the imports directly (including the terminator), since the size of the 43 | // import directory is not necessarily correct (Windows doesn't use it at all). 44 | static DWORD sizeof_imports( LPPROCESS_INFORMATION ppi, 45 | PBYTE pBase, DWORD rImports ) 46 | { 47 | IMAGE_IMPORT_DESCRIPTOR import; 48 | PIMAGE_IMPORT_DESCRIPTOR pImports; 49 | DWORD cnt; 50 | 51 | if (rImports == 0) 52 | return 0; 53 | 54 | pImports = (PIMAGE_IMPORT_DESCRIPTOR)(pBase + rImports); 55 | cnt = 0; 56 | do 57 | { 58 | ++cnt; 59 | ReadProcVar( pImports++, &import ); 60 | } while (import.Name != 0); 61 | 62 | return cnt * sizeof(import); 63 | } 64 | 65 | 66 | void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase ) 67 | { 68 | DWORD rva; 69 | PVOID pMem; 70 | DWORD len, import_size; 71 | DWORD pr; 72 | IMAGE_DOS_HEADER DosHeader; 73 | IMAGE_NT_HEADERS NTHeader, *pNTHeader; 74 | PIMAGE_IMPORT_DESCRIPTOR pImports; 75 | IMAGE_COR20_HEADER ComHeader, *pComHeader; 76 | union 77 | { 78 | PBYTE pB; 79 | PLONG_PTR pL; 80 | PIMAGE_IMPORT_DESCRIPTOR pI; 81 | } ip; 82 | 83 | ReadProcVar( pBase, &DosHeader ); 84 | pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew); 85 | ReadProcVar( pNTHeader, &NTHeader ); 86 | 87 | // Windows 8 and later require the IDT to be part of a section when there's 88 | // no IAT. This means we can't move the imports, so remote load instead. 89 | if (NTHeader.DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT && 90 | get_os_version() >= 0x602) 91 | { 92 | #ifdef _WIN64 93 | RemoteLoad64( ppi ); 94 | #else 95 | RemoteLoad32( ppi ); 96 | #endif 97 | return; 98 | } 99 | 100 | import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress ); 101 | len = 2 * PTRSZ + ansi_len + sizeof(*pImports) + import_size; 102 | pImports = HeapAlloc( hHeap, 0, len ); 103 | if (pImports == NULL) 104 | { 105 | DEBUGSTR( 1, " Failed to allocate memory" ); 106 | return; 107 | } 108 | pMem = FindMem( ppi->hProcess, pBase, len ); 109 | if (pMem == NULL) 110 | { 111 | DEBUGSTR( 1, " Failed to allocate virtual memory (%u)", GetLastError() ); 112 | HeapFree( hHeap, 0, pImports ); 113 | return; 114 | } 115 | rva = (DWORD)((PBYTE)pMem - pBase); 116 | 117 | ip.pL = (PLONG_PTR)pImports; 118 | *ip.pL++ = IMAGE_ORDINAL_FLAG + 1; 119 | *ip.pL++ = 0; 120 | RtlMoveMemory( ip.pB, ansi_dll, ansi_len ); 121 | ip.pB += ansi_len; 122 | ip.pI->OriginalFirstThunk = 0; 123 | ip.pI->TimeDateStamp = 0; 124 | ip.pI->ForwarderChain = 0; 125 | ip.pI->Name = rva + 2 * PTRSZ; 126 | ip.pI->FirstThunk = rva; 127 | ReadProcMem( pBase+NTHeader.IMPORTDIR.VirtualAddress, ip.pI+1, import_size ); 128 | WriteProcMem( pMem, pImports, len ); 129 | HeapFree( hHeap, 0, pImports ); 130 | 131 | // If there's no IAT, copy the original IDT (to allow writable ".idata"). 132 | if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_IAT && 133 | NTHeader.IATDIR.VirtualAddress == 0) 134 | NTHeader.IATDIR = NTHeader.IMPORTDIR; 135 | 136 | NTHeader.IMPORTDIR.VirtualAddress = rva + 2 * PTRSZ + ansi_len; 137 | //NTHeader.IMPORTDIR.Size += sizeof(*pImports); 138 | 139 | // Remove bound imports, so the updated import table is used. 140 | if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT) 141 | { 142 | NTHeader.BOUNDDIR.VirtualAddress = 0; 143 | //NTHeader.BOUNDDIR.Size = 0; 144 | } 145 | 146 | VirtProtVar( pNTHeader, PAGE_READWRITE ); 147 | WriteProcVar( pNTHeader, &NTHeader ); 148 | VirtProtVar( pNTHeader, pr ); 149 | 150 | // Remove the IL-only flag on a managed process. 151 | if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR && 152 | NTHeader.COMDIR.VirtualAddress != 0) 153 | { 154 | pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress); 155 | ReadProcVar( pComHeader, &ComHeader ); 156 | if (ComHeader.Flags & COMIMAGE_FLAGS_ILONLY) 157 | { 158 | ComHeader.Flags &= ~COMIMAGE_FLAGS_ILONLY; 159 | VirtProtVar( pComHeader, PAGE_READWRITE ); 160 | WriteProcVar( pComHeader, &ComHeader ); 161 | VirtProtVar( pComHeader, pr ); 162 | } 163 | } 164 | } 165 | 166 | 167 | #ifdef _WIN64 168 | void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase ) 169 | { 170 | DWORD rva; 171 | PVOID pMem; 172 | DWORD len, import_size; 173 | DWORD pr; 174 | IMAGE_DOS_HEADER DosHeader; 175 | IMAGE_NT_HEADERS32 NTHeader, *pNTHeader; 176 | PIMAGE_IMPORT_DESCRIPTOR pImports; 177 | IMAGE_COR20_HEADER ComHeader, *pComHeader; 178 | union 179 | { 180 | PBYTE pB; 181 | PLONG pL; 182 | PIMAGE_IMPORT_DESCRIPTOR pI; 183 | } ip; 184 | 185 | ReadProcVar( pBase, &DosHeader ); 186 | pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew); 187 | ReadProcVar( pNTHeader, &NTHeader ); 188 | 189 | if (NTHeader.DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT && 190 | get_os_version() >= 0x602) 191 | { 192 | RemoteLoad32( ppi ); 193 | return; 194 | } 195 | 196 | import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress ); 197 | len = 8 + ansi_len + sizeof(*pImports) + import_size; 198 | pImports = HeapAlloc( hHeap, 0, len ); 199 | if (pImports == NULL) 200 | { 201 | DEBUGSTR( 1, " Failed to allocate memory" ); 202 | return; 203 | } 204 | pMem = FindMem( ppi->hProcess, pBase, len ); 205 | if (pMem == NULL) 206 | { 207 | DEBUGSTR( 1, " Failed to allocate virtual memory" ); 208 | HeapFree( hHeap, 0, pImports ); 209 | return; 210 | } 211 | rva = (DWORD)((PBYTE)pMem - pBase); 212 | 213 | ip.pL = (PLONG)pImports; 214 | *ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1; 215 | *ip.pL++ = 0; 216 | RtlMoveMemory( ip.pB, ansi_dll, ansi_len ); 217 | ip.pB += ansi_len; 218 | ip.pI->OriginalFirstThunk = 0; 219 | ip.pI->TimeDateStamp = 0; 220 | ip.pI->ForwarderChain = 0; 221 | ip.pI->Name = rva + 8; 222 | ip.pI->FirstThunk = rva; 223 | ReadProcMem( pBase+NTHeader.IMPORTDIR.VirtualAddress, ip.pI+1, import_size ); 224 | WriteProcMem( pMem, pImports, len ); 225 | HeapFree( hHeap, 0, pImports ); 226 | 227 | if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_IAT && 228 | NTHeader.IATDIR.VirtualAddress == 0) 229 | NTHeader.IATDIR = NTHeader.IMPORTDIR; 230 | NTHeader.IMPORTDIR.VirtualAddress = rva + 8 + ansi_len; 231 | //NTHeader.IMPORTDIR.Size += sizeof(*pImports); 232 | if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT) 233 | { 234 | NTHeader.BOUNDDIR.VirtualAddress = 0; 235 | //NTHeader.BOUNDDIR.Size = 0; 236 | } 237 | VirtProtVar( pNTHeader, PAGE_READWRITE ); 238 | WriteProcVar( pNTHeader, &NTHeader ); 239 | VirtProtVar( pNTHeader, pr ); 240 | 241 | if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR && 242 | NTHeader.COMDIR.VirtualAddress != 0) 243 | { 244 | pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress); 245 | ReadProcVar( pComHeader, &ComHeader ); 246 | if (ComHeader.Flags & COMIMAGE_FLAGS_ILONLY) 247 | { 248 | ComHeader.Flags &= ~COMIMAGE_FLAGS_ILONLY; 249 | VirtProtVar( pComHeader, PAGE_READWRITE ); 250 | WriteProcVar( pComHeader, &ComHeader ); 251 | VirtProtVar( pComHeader, pr ); 252 | } 253 | } 254 | } 255 | #endif 256 | 257 | 258 | /* 259 | Locate the base address of ntdll.dll. This is supposedly really at the same 260 | address for every process, but let's find it anyway. A newly-created 261 | suspended process has two images in memory: the process itself and ntdll.dll. 262 | Thus the one that is a DLL must be ntdll.dll. However, a WOW64 process also 263 | has the 64-bit version, so test the machine. 264 | */ 265 | #ifdef _WIN64 266 | static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi, WORD machine ) 267 | #else 268 | static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi ) 269 | #endif 270 | { 271 | PBYTE ptr; 272 | MEMORY_BASIC_INFORMATION minfo; 273 | IMAGE_DOS_HEADER dos_header; 274 | IMAGE_NT_HEADERS nt_header; 275 | 276 | for (ptr = NULL; 277 | VirtualQueryEx( ppi->hProcess, ptr, &minfo, sizeof(minfo) ); 278 | ptr += minfo.RegionSize) 279 | { 280 | if (minfo.BaseAddress == minfo.AllocationBase 281 | && ReadProcVar( minfo.BaseAddress, &dos_header ) 282 | && dos_header.e_magic == IMAGE_DOS_SIGNATURE 283 | && ReadProcVar( (PBYTE)minfo.BaseAddress + dos_header.e_lfanew, 284 | &nt_header ) 285 | && nt_header.Signature == IMAGE_NT_SIGNATURE 286 | && (nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL) 287 | #ifdef _WIN64 288 | && nt_header.FileHeader.Machine == machine 289 | #endif 290 | ) 291 | { 292 | return minfo.BaseAddress; 293 | } 294 | } 295 | 296 | DEBUGSTR( 1, " Failed to find ntdll.dll!" ); 297 | return NULL; 298 | } 299 | 300 | 301 | #ifdef _WIN64 302 | void RemoteLoad64( LPPROCESS_INFORMATION ppi ) 303 | { 304 | PBYTE ntdll; 305 | DWORD rLdrLoadDll; 306 | PBYTE pMem; 307 | DWORD len; 308 | HANDLE thread; 309 | BYTE code[64]; 310 | union 311 | { 312 | PBYTE pB; 313 | PUSHORT pS; 314 | PDWORD pD; 315 | PBYTE* pL; 316 | } ip; 317 | 318 | ntdll = get_ntdll( ppi, IMAGE_FILE_MACHINE_AMD64 ); 319 | if (ntdll == NULL) 320 | return; 321 | 322 | rLdrLoadDll = GetProcRVA( L"ntdll.dll", "LdrLoadDll", 64 ); 323 | if (rLdrLoadDll == 0) 324 | return; 325 | 326 | pMem = VirtualAllocEx( ppi->hProcess, NULL, 4096, MEM_COMMIT, 327 | PAGE_EXECUTE_READ ); 328 | if (pMem == NULL) 329 | { 330 | DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError()); 331 | return; 332 | } 333 | 334 | len = (DWORD)TSIZE(lstrlen( DllName ) + 1); 335 | ip.pB = code; 336 | 337 | *ip.pL++ = ntdll + rLdrLoadDll; // address of LdrLoadDll 338 | *ip.pD++ = 0x38ec8348; // sub rsp, 0x38 339 | *ip.pD++ = 0x244c8d4c; // lea r9, [rsp+0x20] 340 | *ip.pD++ = 0x058d4c20; // lea r8, L"path\to\ANSI64.dll" 341 | *ip.pD++ = 16; // xor edx, edx 342 | *ip.pD++ = 0xc933d233; // xor ecx, ecx 343 | *ip.pS++ = 0x15ff; // call LdrLoadDll 344 | *ip.pD++ = -34; // add rsp, 0x38 345 | *ip.pD++ = 0x38c48348; // ret 346 | *ip.pS++ = 0x00c3; // alignment for the name 347 | *ip.pS++ = (USHORT)(len - TSIZE(1)); // UNICODE_STRING.Length 348 | *ip.pS++ = (USHORT)len; // UNICODE_STRING.MaximumLength 349 | *ip.pD++ = 0; // padding 350 | *ip.pL++ = pMem + 56; // UNICODE_STRING.Buffer 351 | WriteProcMem( pMem, code, ip.pB - code ); 352 | *(PDWORD)DllNameType = 0x340036/*L'46'*/; 353 | WriteProcMem( pMem + (ip.pB - code), DllName, len ); 354 | thread = CreateRemoteThread( ppi->hProcess, NULL, 4096, 355 | (LPTHREAD_START_ROUTINE)(pMem + 8), NULL, 0, NULL ); 356 | WaitForSingleObject( thread, INFINITE ); 357 | CloseHandle( thread ); 358 | VirtualFreeEx( ppi->hProcess, pMem, 0, MEM_RELEASE ); 359 | } 360 | #endif 361 | 362 | 363 | void RemoteLoad32( LPPROCESS_INFORMATION ppi ) 364 | { 365 | PBYTE ntdll; 366 | DWORD rLdrLoadDll; 367 | PBYTE pMem; 368 | DWORD bMem; 369 | DWORD len; 370 | HANDLE thread; 371 | BYTE code[64]; 372 | union 373 | { 374 | PBYTE pB; 375 | PUSHORT pS; 376 | PDWORD pD; 377 | } ip; 378 | 379 | #ifdef _WIN64 380 | ntdll = get_ntdll( ppi, IMAGE_FILE_MACHINE_I386 ); 381 | #else 382 | ntdll = get_ntdll( ppi ); 383 | #endif 384 | if (ntdll == NULL) 385 | return; 386 | 387 | #ifdef _WIN64 388 | rLdrLoadDll = GetProcRVA( L"ntdll.dll", "LdrLoadDll", 32 ); 389 | #else 390 | rLdrLoadDll = GetProcRVA( L"ntdll.dll", "LdrLoadDll" ); 391 | #endif 392 | if (rLdrLoadDll == 0) 393 | return; 394 | 395 | pMem = VirtualAllocEx( ppi->hProcess, NULL, 4096, MEM_COMMIT, 396 | PAGE_EXECUTE_READ ); 397 | if (pMem == NULL) 398 | { 399 | DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError()); 400 | return; 401 | } 402 | bMem = PtrToUint( pMem ); 403 | 404 | len = (DWORD)TSIZE(lstrlen( DllName ) + 1); 405 | ip.pB = code; 406 | 407 | *ip.pS++ = 0x5451; // push ecx esp 408 | *ip.pB++ = 0x68; // push 409 | *ip.pD++ = bMem + 20; // L"path\to\ANSI32.dll" 410 | *ip.pD++ = 0x006A006A; // push 0 0 411 | *ip.pB++ = 0xe8; // call LdrLoadDll 412 | *ip.pD++ = PtrToUint( ntdll ) + rLdrLoadDll - (bMem + 16); 413 | *ip.pD++ = 0xc359; // pop ecx / ret and padding 414 | *ip.pS++ = (USHORT)(len - TSIZE(1)); // UNICODE_STRING.Length 415 | *ip.pS++ = (USHORT)len; // UNICODE_STRING.MaximumLength 416 | *ip.pD++ = bMem + 28; // UNICODE_STRING.Buffer 417 | WriteProcMem( pMem, code, ip.pB - code ); 418 | #ifdef _WIN64 419 | *(PDWORD)DllNameType = 0x320033/*L'23'*/; 420 | #endif 421 | WriteProcMem( pMem + (ip.pB - code), DllName, len ); 422 | thread = CreateRemoteThread( ppi->hProcess, NULL, 4096, 423 | (LPTHREAD_START_ROUTINE)pMem, NULL, 0, NULL ); 424 | WaitForSingleObject( thread, INFINITE ); 425 | CloseHandle( thread ); 426 | VirtualFreeEx( ppi->hProcess, pMem, 0, MEM_RELEASE ); 427 | } 428 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | util.c - Utility functions. 3 | */ 4 | 5 | #include "ansicon.h" 6 | #include "version.h" 7 | 8 | 9 | TCHAR prog_path[MAX_PATH]; 10 | LPTSTR prog; 11 | 12 | int log_level; 13 | 14 | TCHAR DllName[MAX_PATH]; // Dll file name 15 | char ansi_dll[MAX_PATH]; 16 | DWORD ansi_len; 17 | #ifdef _WIN64 18 | char* ansi_bits; 19 | #endif 20 | 21 | 22 | // Get just the name of the program: "C:\path\program.exe" -> "program". 23 | // Returns a pointer within program; it is modified to remove the extension. 24 | LPTSTR get_program_name( LPTSTR program ) 25 | { 26 | LPTSTR name, ext; 27 | 28 | if (program == NULL) 29 | { 30 | GetModuleFileName( NULL, prog_path, lenof(prog_path) ); 31 | program = prog_path; 32 | } 33 | name = ac_wcsrchr( program, '\\' ); 34 | if (name != NULL) 35 | ++name; 36 | else 37 | name = program; 38 | ext = ac_wcsrchr( name, '.' ); 39 | if (ext != NULL && ext != name) 40 | *ext = '\0'; 41 | 42 | return name; 43 | } 44 | 45 | 46 | // Get the ANSI path of the DLL for the import. If it can't be converted, 47 | // just use the name and hope it's on the PATH. 48 | void set_ansi_dll( void ) 49 | { 50 | BOOL bad; 51 | 52 | ansi_len = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, DllName, -1, 53 | NULL, 0, NULL, &bad ); 54 | if (bad || ansi_len > MAX_PATH) 55 | { 56 | #ifdef _WIN64 57 | ansi_bits = ansi_dll + 4; 58 | if (*DllNameType == '6') 59 | memcpy( ansi_dll, "ANSI64.dll\0", 12 ); 60 | else 61 | #endif 62 | memcpy( ansi_dll, "ANSI32.dll\0", 12 ); 63 | ansi_len = 12; 64 | } 65 | else 66 | { 67 | WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, DllName, -1, 68 | ansi_dll, MAX_PATH, NULL, NULL ); 69 | #ifdef _WIN64 70 | ansi_bits = ansi_dll + ansi_len - 7; 71 | #endif 72 | ansi_len = (ansi_len + 3) & ~3; 73 | } 74 | } 75 | 76 | 77 | // GetVersion and GetVersionEx use Win32VersionValue from the header, which 78 | // could be anything. Retrieve the OS version from NTDLL's header. 79 | DWORD get_os_version( void ) 80 | { 81 | PIMAGE_DOS_HEADER pDosHeader; 82 | PIMAGE_NT_HEADERS pNTHeader; 83 | 84 | pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( L"ntdll.dll" ); 85 | pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); 86 | return pNTHeader->OptionalHeader.MajorOperatingSystemVersion << 8 | 87 | pNTHeader->OptionalHeader.MinorOperatingSystemVersion; 88 | } 89 | 90 | 91 | static LPSTR buf; 92 | static DWORD buf_len; 93 | static BOOL quote, alt; 94 | 95 | static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len ) 96 | { 97 | static UINT cp; 98 | static DWORD flags; 99 | static BOOL def, *pDef, start_trail; 100 | union 101 | { 102 | LPSTR a; 103 | LPWSTR w; 104 | } src; 105 | int ch; 106 | BOOL trail; 107 | 108 | src.a = (LPSTR)str; 109 | if (len == 0 && str != 0) 110 | len = (DWORD)(wide ? lstrlen( src.w ) : strlen( src.a )); 111 | 112 | if (pos + len * 6 + 8 >= buf_len) 113 | { 114 | LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + len * 6 + 8 ); 115 | if (tmp == NULL) 116 | return pos; 117 | buf = tmp; 118 | buf_len = (DWORD)HeapSize( hHeap, 0, buf ); 119 | } 120 | 121 | if (len == 0) 122 | { 123 | if (str == 0) 124 | { 125 | memcpy( buf + pos, "", 6 ); 126 | pos += 6; 127 | } 128 | else if (quote) 129 | { 130 | buf[pos++] = '"'; 131 | buf[pos++] = '"'; 132 | } 133 | else if (alt) 134 | { 135 | memcpy( buf + pos, "", 7 ); 136 | pos += 7; 137 | } 138 | return pos; 139 | } 140 | 141 | if (cp != GetConsoleOutputCP()) 142 | { 143 | cp = GetConsoleOutputCP(); 144 | if (wide) 145 | { 146 | wchar_t und = L'\xFFFF'; 147 | flags = WC_NO_BEST_FIT_CHARS; 148 | pDef = &def; 149 | // Some code pages don't support the default character. 150 | if (!WideCharToMultiByte( cp, flags, &und, 1, buf + pos, 12, NULL, pDef )) 151 | { 152 | flags = 0; 153 | pDef = NULL; 154 | def = FALSE; 155 | } 156 | } 157 | } 158 | 159 | if (quote) 160 | buf[pos++] = '"'; 161 | 162 | trail = FALSE; 163 | while (len-- != 0) 164 | { 165 | if (wide) 166 | ch = *src.w++; 167 | else 168 | ch = (BYTE)*src.a++; 169 | 170 | if (ch < 32 || (quote && start_trail)) 171 | { 172 | start_trail = FALSE; 173 | if (quote) 174 | { 175 | buf[pos++] = '\\'; 176 | switch (ch) 177 | { 178 | case '\0': buf[pos++] = '0'; break; 179 | case '\a': buf[pos++] = 'a'; break; 180 | case '\b': buf[pos++] = 'b'; break; 181 | case '\t': buf[pos++] = 't'; break; 182 | case '\n': buf[pos++] = 'n'; break; 183 | case '\v': buf[pos++] = 'v'; break; 184 | case '\f': buf[pos++] = 'f'; break; 185 | case '\r': buf[pos++] = 'r'; break; 186 | case 27 : buf[pos++] = 'e'; break; 187 | default: 188 | pos += ac_sprintf( buf + pos, "x%2X", ch ); 189 | } 190 | } 191 | else 192 | { 193 | buf[pos++] = '^'; 194 | buf[pos++] = ch + '@'; 195 | } 196 | } 197 | else if (quote && ch == '"') 198 | { 199 | buf[pos++] = '\\'; 200 | buf[pos++] = ch; 201 | } 202 | else if (!wide) 203 | { 204 | if (quote && (cp == 932 || cp == 936 || cp == 949 || cp == 950)) 205 | { 206 | if (trail) 207 | trail = FALSE; 208 | else if (IsDBCSLeadByteEx( cp, (char)ch )) 209 | { 210 | if (len == 0) 211 | start_trail = TRUE; 212 | else 213 | trail = TRUE; 214 | } 215 | } 216 | if (quote && start_trail) 217 | pos += ac_sprintf( buf + pos, "\\x%2X", ch ); 218 | else 219 | buf[pos++] = ch; 220 | } 221 | else 222 | { 223 | int mb = WideCharToMultiByte( cp, flags, src.w - 1, 1, buf + pos, 12, 224 | NULL, pDef ); 225 | if (def) 226 | { 227 | buf[pos++] = (quote) ? '\\' : '^'; 228 | mb = ac_sprintf( buf + pos, ch < 0x100 ? "x%2X" : "u%4X", ch ); 229 | } 230 | pos += mb; 231 | } 232 | } 233 | 234 | if (quote) 235 | buf[pos++] = '"'; 236 | 237 | return pos; 238 | } 239 | 240 | void DEBUGSTR( int level, LPCSTR szFormat, ... ) 241 | { 242 | static int prefix_len; 243 | static HANDLE mutex; 244 | static DWORD size; 245 | 246 | WCHAR temp[MAX_PATH]; 247 | HANDLE file; 248 | va_list pArgList; 249 | DWORD len, slen, written; 250 | DWORD_PTR num; 251 | 252 | if ((log_level & 3) < level && !(level & 4 & log_level)) 253 | return; 254 | 255 | if (mutex == NULL) 256 | { 257 | mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); 258 | if (mutex == NULL) 259 | { 260 | log_level = 0; 261 | return; 262 | } 263 | buf = HeapAlloc( hHeap, 0, 2048 ); 264 | buf_len = (DWORD)HeapSize( hHeap, 0, buf ); 265 | prefix_len = str_format( 0, TRUE, (DWORD_PTR)prog, 0 ); 266 | prefix_len += ac_sprintf(buf+prefix_len, " (%u): ", GetCurrentProcessId()); 267 | } 268 | if (WaitForSingleObject( mutex, 500 ) == WAIT_TIMEOUT) 269 | return; 270 | 271 | ExpandEnvironmentStrings( L"%TEMP%\\ansicon.log", temp, lenof(temp) ); 272 | file = CreateFile( temp, GENERIC_WRITE, FILE_SHARE_READ, NULL, 273 | (szFormat != NULL || (log_level & 8)) ? OPEN_ALWAYS 274 | : CREATE_ALWAYS, 275 | 0, NULL ); 276 | if (file == INVALID_HANDLE_VALUE) 277 | { 278 | ReleaseMutex( mutex ); 279 | return; 280 | } 281 | 282 | len = SetFilePointer( file, 0, NULL, FILE_END ); 283 | if (len == 0 || szFormat == NULL) 284 | { 285 | char buf[128]; 286 | SYSTEMTIME now; 287 | 288 | size = 0; 289 | 290 | if (len != 0) 291 | { 292 | RtlFillMemory( buf + 2, 72, '=' ); 293 | buf[0] = buf[74] = buf[76] = '\r'; 294 | buf[1] = buf[75] = buf[77] = '\n'; 295 | WriteFile( file, buf, 78, &written, NULL ); 296 | } 297 | 298 | GetLocalTime( &now ); 299 | len = ac_sprintf( buf, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d)" 300 | " started %d-%2d-%2d %d:%2d:%2d\r\n", 301 | log_level, 302 | now.wYear, now.wMonth, now.wDay, 303 | now.wHour, now.wMinute, now.wSecond ); 304 | WriteFile( file, buf, len, &written, NULL ); 305 | if (szFormat == NULL) 306 | { 307 | CloseHandle( file ); 308 | ReleaseMutex( mutex ); 309 | return; 310 | } 311 | } 312 | if (len != size) 313 | WriteFile( file, "\r\n", 2, &written, NULL ); 314 | 315 | va_start( pArgList, szFormat ); 316 | 317 | // Customized printf, mainly to handle wide-character strings the way I want. 318 | // It only supports: 319 | // %u unsigned 32-bit decimal 320 | // %X unsigned 32-bit upper case hexadecimal 321 | // %p native pointer 322 | // %q native pointer, display as 32 bits 323 | // %P 32-bit pointer, display as 64 bits 324 | // %s null-terminated byte characters 325 | // %S null-terminated wide characters 326 | // 327 | // s & S may be prefixed with (in this order): 328 | // " quote the string, using C-style escapes and for NULL 329 | // # use for NULL and for "" 330 | // < length of the string is the previous %u 331 | // * length of the string is the parameter before the string 332 | // 333 | // Wide strings are converted according to the current code page; if a 334 | // character could not be translated, hex is used. 335 | // 336 | // C-style escapes are the standard backslash sequences, plus '\e' for ESC, 337 | // with '\x' used for two hex digits and '\u' for four. Otherwise, caret 338 | // notation is used to represent controls, with '^x'/'^u' for hex. 339 | 340 | num = 0; 341 | len = prefix_len; 342 | while (*szFormat != '\0') 343 | { 344 | if (*szFormat != '%') 345 | buf[len++] = *szFormat++; 346 | else 347 | { 348 | quote = alt = FALSE; 349 | ++szFormat; 350 | if (*szFormat == '"') 351 | { 352 | quote = TRUE; 353 | ++szFormat; 354 | } 355 | if (*szFormat == '#') 356 | { 357 | alt = TRUE; 358 | ++szFormat; 359 | } 360 | slen = 0; 361 | if (*szFormat == '<') 362 | { 363 | slen = (DWORD)num; 364 | ++szFormat; 365 | } 366 | if (*szFormat == '*') 367 | { 368 | slen = va_arg( pArgList, DWORD ); 369 | ++szFormat; 370 | } 371 | num = va_arg( pArgList, DWORD_PTR ); 372 | switch (*szFormat++) 373 | { 374 | case 'u': len += ac_sprintf( buf + len, "%u", (DWORD)num ); break; 375 | case 'X': len += ac_sprintf( buf + len, "%X", (DWORD)num ); break; 376 | case 'p': 377 | #ifdef _WIN64 378 | len += ac_sprintf( buf + len, "%8X_%8X", 379 | (DWORD)(num >> 32), (DWORD)num ); 380 | break; 381 | #endif 382 | case 'q': len += ac_sprintf( buf + len, "%8X", (DWORD)num ); break; 383 | case 'P': len += ac_sprintf( buf + len, "00000000_%8X", (DWORD)num ); break; 384 | case 's': len = str_format( len, FALSE, num, slen ); break; 385 | case 'S': len = str_format( len, TRUE, num, slen ); break; 386 | default: 387 | buf[len++] = '%'; 388 | if (szFormat[-1] == '\0') 389 | --szFormat; 390 | else 391 | buf[len++] = szFormat[-1]; 392 | } 393 | if (len >= buf_len - 20) 394 | { 395 | LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + 128 ); 396 | if (tmp == NULL) 397 | break; 398 | buf = tmp; 399 | buf_len = (DWORD)HeapSize( hHeap, 0, buf ); 400 | } 401 | } 402 | } 403 | buf[len++] = '\r'; 404 | buf[len++] = '\n'; 405 | 406 | WriteFile( file, buf, len, &written, NULL ); 407 | 408 | size = GetFileSize( file, NULL ); 409 | CloseHandle( file ); 410 | ReleaseMutex( mutex ); 411 | } 412 | 413 | 414 | // Provide custom versions of used C runtime functions, to remove dependence on 415 | // the runtime library. 416 | 417 | // For my purposes (palette index and colors): 418 | // * no leading space; 419 | // * base is 10 or 16; 420 | // * number doesn't overflow. 421 | unsigned long ac_wcstoul( const wchar_t* str, wchar_t** end, int base ) 422 | { 423 | unsigned c, n; 424 | unsigned long num = 0; 425 | 426 | for (;;) 427 | { 428 | n = -1; 429 | c = *str; 430 | if (c >= '0' && c <= '9') 431 | n = c - '0'; 432 | else if (base == 16) 433 | { 434 | c |= 0x20; 435 | if (c >= 'a' && c <= 'f') 436 | n = c - 'a' + 10; 437 | } 438 | if (n == -1) 439 | break; 440 | 441 | num = num * base + n; 442 | ++str; 443 | } 444 | 445 | if (end != NULL) 446 | *end = (wchar_t*)str; 447 | return num; 448 | } 449 | 450 | 451 | // For my purposes (log level): 452 | // * same as ac_wcstoul. 453 | int ac_wtoi( const wchar_t* str ) 454 | { 455 | return (int)ac_wcstoul( str, NULL, 10 ); 456 | } 457 | 458 | 459 | // For my purposes (default attribute): 460 | // * same as ac_wcstoul. 461 | long ac_wcstol( const wchar_t* str, wchar_t** end, int base ) 462 | { 463 | int neg = (*str == '-'); 464 | long num = ac_wcstoul( str + neg, end, base ); 465 | return neg ? -num : num; 466 | } 467 | 468 | 469 | // For my purposes (program separator): 470 | // * set is only one or two characters. 471 | wchar_t* ac_wcspbrk( const wchar_t* str, const wchar_t* set ) 472 | { 473 | while (*str != '\0') 474 | { 475 | if (*str == set[0] || *str == set[1]) 476 | return (wchar_t*)str; 477 | ++str; 478 | } 479 | return NULL; 480 | } 481 | 482 | 483 | // For my purposes (path components): 484 | // * c is not null. 485 | wchar_t* ac_wcsrchr( const wchar_t* str, wchar_t c ) 486 | { 487 | wchar_t* last = NULL; 488 | 489 | while (*str != '\0') 490 | { 491 | if (*str == c) 492 | last = (wchar_t*)str; 493 | ++str; 494 | } 495 | 496 | return last; 497 | } 498 | 499 | 500 | // For my purposes (import module matching): 501 | // * A-Z becomes a-z; 502 | // * s2 is lower case; 503 | // * both strings are at least LEN long; 504 | // * returns 0 for match, 1 for no match. 505 | int ac_strnicmp( const char* s1, const char* s2, size_t len ) 506 | { 507 | while (len--) 508 | { 509 | if (*s1 != *s2) 510 | { 511 | if (*s2 < 'a' || *s2 > 'z' || (*s1 | 0x20) != *s2) 512 | return 1; 513 | } 514 | ++s1; 515 | ++s2; 516 | } 517 | return 0; 518 | } 519 | 520 | 521 | static const char hex[16] = { '0','1','2','3','4','5','6','7', 522 | '8','9','A','B','C','D','E','F' }; 523 | 524 | // For my purposes: 525 | // * BUF is big enough; 526 | // * FMT is valid; 527 | // * width implies zero fill and the number is not bigger than the width; 528 | // * only types d, u & X are supported, all as 32-bit unsigned; 529 | // * BUF is NOT NUL-terminated. 530 | int ac_sprintf( char* buf, const char* fmt, ... ) 531 | { 532 | va_list args; 533 | DWORD num; 534 | int t, width; 535 | char* beg = buf; 536 | 537 | va_start( args, fmt ); 538 | 539 | while (*fmt) 540 | { 541 | t = *fmt++; 542 | if (t != '%') 543 | *buf++ = t; 544 | else 545 | { 546 | num = va_arg( args, DWORD ); 547 | t = *fmt++; 548 | width = 0; 549 | if (t == '2' || t == '4' || t == '8') 550 | { 551 | width = t - '0'; 552 | t = *fmt++; 553 | } 554 | if (t == 'X') 555 | { 556 | int bits; 557 | if (width == 0) 558 | { 559 | if (num & 0xF0000000) 560 | bits = 32; 561 | else 562 | { 563 | bits = 4; 564 | while (num >> bits) 565 | bits += 4; 566 | } 567 | } 568 | else 569 | bits = width * 4; 570 | do 571 | { 572 | bits -= 4; 573 | *buf++ = hex[num >> bits & 0xF]; 574 | } while (bits); 575 | } 576 | else // (t == 'd' || t == 'u') 577 | { 578 | if (width == 2) 579 | { 580 | *buf++ = (int)(num / 10) + '0'; 581 | *buf++ = (int)(num % 10) + '0'; 582 | } 583 | else 584 | { 585 | unsigned power; 586 | if (num >= 1000000000) 587 | power = 1000000000; 588 | else 589 | { 590 | power = 1; 591 | while (num / (power * 10)) 592 | power *= 10; 593 | } 594 | do 595 | { 596 | *buf++ = num / power % 10 + '0'; 597 | power /= 10; 598 | } while (power); 599 | } 600 | } 601 | } 602 | } 603 | 604 | return (int)(buf - beg); 605 | } 606 | 607 | 608 | // For my purposes: 609 | // * BUF is big enough; 610 | // * FMT is valid; 611 | // * width is only for X, is only 2 and the number is not bigger than that; 612 | // * X, d & u are 32-bit unsigned decimal; 613 | // * c is not output if NUL; 614 | // * no other type is used; 615 | // * return value is not used. 616 | int ac_wprintf( wchar_t* buf, const char* fmt, ... ) 617 | { 618 | va_list args; 619 | DWORD num; 620 | int t; 621 | 622 | va_start( args, fmt ); 623 | 624 | while (*fmt) 625 | { 626 | t = *fmt++; 627 | if (t != '%') 628 | *buf++ = t; 629 | else 630 | { 631 | num = va_arg( args, DWORD ); 632 | t = *fmt++; 633 | if (t == '2') 634 | { 635 | ++fmt; 636 | *buf++ = hex[num >> 4]; 637 | *buf++ = hex[num & 0xF]; 638 | } 639 | else if (t == 'X') 640 | { 641 | int bits; 642 | if (num & 0xF0000000) 643 | bits = 32; 644 | else 645 | { 646 | bits = 4; 647 | while (num >> bits) 648 | bits += 4; 649 | } 650 | do 651 | { 652 | bits -= 4; 653 | *buf++ = hex[num >> bits & 0xF]; 654 | } while (bits); 655 | } 656 | else if (t == 'c') 657 | { 658 | if (num) 659 | *buf++ = (wchar_t)num; 660 | } 661 | else // (t == 'd' || t == 'u') 662 | { 663 | unsigned power; 664 | if (num >= 1000000000) 665 | power = 1000000000; 666 | else 667 | { 668 | power = 1; 669 | while (num / (power * 10)) 670 | power *= 10; 671 | } 672 | do 673 | { 674 | *buf++ = num / power % 10 + '0'; 675 | power /= 10; 676 | } while (power); 677 | } 678 | } 679 | } 680 | *buf = '\0'; 681 | 682 | return 0; 683 | } 684 | -------------------------------------------------------------------------------- /ansicon.c: -------------------------------------------------------------------------------- 1 | /* 2 | ANSICON.c - ANSI escape sequence console driver. 3 | 4 | Jason Hood, 21 to 23 October, 2005. 5 | 6 | Original injection code was derived from Console Manager by Sergey Oblomov 7 | (hoopoepg). Use of FlushInstructionCache came from www.catch22.net. 8 | Additional information came from "Process-wide API spying - an ultimate hack", 9 | Anton Bassov's article in "The Code Project" (use of OpenThread). 10 | 11 | v1.01, 11 & 12 March, 2006: 12 | -m option to set "monochrome" (grey on black); 13 | restore original color on exit. 14 | 15 | v1.10, 22 February, 2009: 16 | ignore Ctrl+C/Ctrl+Break. 17 | 18 | v1.13, 21 & 27 March, 2009: 19 | alternate injection method, to work with DEP; 20 | use Unicode. 21 | 22 | v1.20, 17 to 21 June, 2009: 23 | use a combination of the two injection methods; 24 | test if ANSICON is already installed; 25 | added -e (and -E) option to echo the command line (without newline); 26 | added -t (and -T) option to type (display) files (with file name). 27 | 28 | v1.21, 23 September, 2009: 29 | added -i (and -u) to add (remove) ANSICON to AutoRun. 30 | 31 | v1.24, 6 & 7 January, 2010: 32 | no arguments to -t, or using "-" for the name, will read from stdin; 33 | fix -t and -e when ANSICON was already loaded. 34 | 35 | v1.25, 22 July, 2010: 36 | added -IU for HKLM. 37 | 38 | v1.30, 3 August to 7 September, 2010: 39 | x64 support. 40 | 41 | v1.31, 13 & 15 November, 2010: 42 | use LLW to fix potential Unicode path problems; 43 | VC compatibility (2008 Express for 32-bit, PSDK 2003 R2 for 64-bit); 44 | explicitly use wide characters (stick with TCHAR, but not ). 45 | 46 | v1.32, 4 to 22 December, 2010: 47 | make -p more robust; 48 | inject into GUI processes; 49 | -i implies -p. 50 | 51 | v1.50, 7 to 14 December, 2011: 52 | -u does not imply -p; 53 | add the PID to the debugging output; 54 | use ANSICON_VER to test if already installed; 55 | always place first in AutoRun; 56 | logging is always available, controlled by ANSICON_LOG environment variable; 57 | only restore the original color after program/echo/type; 58 | return program's exit code. 59 | 60 | 7 January, 2012: 61 | fixed installing into a piped CMD.EXE; 62 | added a log message indicating all imports have been processed. 63 | 64 | v1.52, 10 April, 2012: 65 | fixed running "cmd" if "ComSpec" is not defined; 66 | pass process & thread identifiers on the command line (for x86->x64). 67 | 68 | v1.60, 22 & 24 November, 2012: 69 | set the code page to convert strings correctly; 70 | expand wildcards for -t; 71 | write the date if appending to the log. 72 | 73 | v1.62, 18 July, 2013: 74 | write the bits to the log; 75 | test if creating the registry key fails (HKLM requires admin privileges). 76 | 77 | v1.63, 25 July, 2013: 78 | don't write the reset sequence if output is redirected. 79 | 80 | v1.70, 31 January to 7 February, 2014: 81 | restore the original (current, not default) attributes if using ansicon.exe 82 | when it's already installed; 83 | use ANSICON_DEF if defined and -m not given; 84 | -e and -t will not output anything if the DLL could not load; 85 | use Unicode output (_O_U16TEXT, for compilers/systems that support it); 86 | log: 64-bit addresses get an underscore between the 8-digit groups; 87 | add error codes to some message. 88 | 89 | v1.80, 28 October & 30 November, 2017: 90 | write newline with _putws, not putwchar (fixes redirecting to CON); 91 | use -pu to unload from the parent. 92 | 93 | v1.84, 7 May, 2018: 94 | import the DLL. 95 | 96 | v1.85, 22 & 23 August, 2018: 97 | use IsConsoleHandle for my_fputws, to distinguish NUL; 98 | don't load into the parent if already loaded; 99 | add log level 32 to log CreateFile. 100 | */ 101 | 102 | #define PDATE L"29 April, 2019" 103 | 104 | #include "ansicon.h" 105 | #include "version.h" 106 | #include 107 | #include 108 | #include 109 | #include 110 | 111 | #ifndef _O_U16TEXT 112 | #define _O_U16TEXT 0x20000 113 | #endif 114 | 115 | #ifdef __MINGW32__ 116 | int _CRT_glob = 0; 117 | #endif 118 | 119 | 120 | #define CMDKEY L"Software\\Microsoft\\Command Processor" 121 | #define AUTORUN L"AutoRun" 122 | 123 | 124 | void help( void ); 125 | 126 | void display( LPCTSTR, BOOL ); 127 | void print_error( LPCTSTR ); 128 | LPTSTR skip_spaces( LPTSTR ); 129 | void get_arg( LPTSTR, LPTSTR*, LPTSTR* ); 130 | void get_file( LPTSTR, LPTSTR*, LPTSTR* ); 131 | 132 | void process_autorun( TCHAR ); 133 | 134 | BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ); 135 | BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR ); 136 | 137 | 138 | static HANDLE hConOut; 139 | 140 | 141 | // The fputws function in MSVCRT.DLL (Windows 7 x64) is broken for Unicode 142 | // output (it just writes the first character). VC6 & 7 don't support Unicode 143 | // output at all (just converting to ANSI) and even when it is supported, it 144 | // just writes single characters (as does _putws & fwprintf). So what the 145 | // heck, DIY. 146 | int my_fputws( const wchar_t* s, FILE* f ) 147 | { 148 | if (IsConsoleHandle( (HANDLE)_get_osfhandle( _fileno( f ) ) )) 149 | { 150 | DWORD written; 151 | WriteConsole( hConOut, s, (DWORD)wcslen( s ), &written, NULL ); 152 | } 153 | else 154 | { 155 | fputws( s, f ); 156 | } 157 | return 0; 158 | } 159 | 160 | #define fputws my_fputws 161 | #define _putws( s ) my_fputws( s L"\n", stdout ) 162 | 163 | 164 | // Use CreateRemoteThread to (un)load our DLL in the target process. 165 | void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app, BOOL unload ) 166 | { 167 | HANDLE hSnap; 168 | MODULEENTRY32 me; 169 | PBYTE proc; 170 | DWORD rva; 171 | BOOL fOk; 172 | LPVOID param; 173 | HANDLE thread; 174 | DWORD ticks; 175 | #ifdef _WIN64 176 | BOOL WOW64; 177 | int type; 178 | #endif 179 | 180 | DEBUGSTR( 1, "Parent = %S (%u)", app, ppi->dwProcessId ); 181 | 182 | // Find the base address of kernel32.dll. 183 | ticks = GetTickCount(); 184 | while ((hSnap = CreateToolhelp32Snapshot( 185 | TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ppi->dwProcessId )) 186 | == INVALID_HANDLE_VALUE) 187 | { 188 | DWORD err = GetLastError(); 189 | #ifndef _WIN64 190 | if (err == ERROR_PARTIAL_COPY) 191 | { 192 | DEBUGSTR( 1, " Ignoring 64-bit process (use x64\\ansicon)" ); 193 | fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr ); 194 | return; 195 | } 196 | #endif 197 | // I really don't think this would happen, but if it does, give up after 198 | // two seconds to avoid a potentially infinite loop. 199 | if (err == ERROR_BAD_LENGTH && GetTickCount() - ticks < 2000) 200 | { 201 | Sleep( 1 ); 202 | continue; 203 | } 204 | DEBUGSTR( 1, " Unable to create snapshot (%u)", err ); 205 | no_go: 206 | fputws( L"ANSICON: unable to inject into parent.\n", stderr ); 207 | return; 208 | } 209 | proc = param = NULL; 210 | #ifdef _WIN64 211 | type = 64; 212 | if (IsWow64Process( ppi->hProcess, &WOW64 ) && WOW64) 213 | { 214 | type = 32; 215 | *(PDWORD)DllNameType = 0x320033/*L'23'*/; 216 | } 217 | #endif 218 | me.dwSize = sizeof(MODULEENTRY32); 219 | for (fOk = Module32First( hSnap, &me ); fOk; fOk = Module32Next( hSnap, &me )) 220 | { 221 | if (_wcsicmp( me.szModule, L"kernel32.dll" ) == 0) 222 | { 223 | proc = me.modBaseAddr; 224 | if (param) 225 | break; 226 | } 227 | else 228 | { 229 | #ifdef _WIN64 230 | if (_wcsicmp( me.szModule, DllNameType - 4 ) == 0) 231 | #else 232 | if (_wcsicmp( me.szModule, L"ANSI32.dll" ) == 0) 233 | #endif 234 | { 235 | param = me.modBaseAddr; 236 | if (proc) 237 | break; 238 | } 239 | } 240 | } 241 | CloseHandle( hSnap ); 242 | if (proc == NULL) 243 | { 244 | DEBUGSTR( 1, " Unable to locate kernel32.dll" ); 245 | goto no_go; 246 | } 247 | if (unload && param == NULL) 248 | { 249 | DEBUGSTR( 1, " Unable to locate ANSICON's DLL" ); 250 | return; 251 | } 252 | else if (!unload && param != NULL) 253 | { 254 | DEBUGSTR( 1, " ANSICON already loaded" ); 255 | return; 256 | } 257 | 258 | #ifdef _WIN64 259 | rva = GetProcRVA( L"kernel32.dll", (unload) ? "FreeLibrary" 260 | : "LoadLibraryW", type ); 261 | #else 262 | rva = GetProcRVA( L"kernel32.dll", unload ? "FreeLibrary" : "LoadLibraryW" ); 263 | #endif 264 | if (rva == 0) 265 | goto no_go; 266 | proc += rva; 267 | 268 | if (!unload) 269 | { 270 | DWORD len = TSIZE((DWORD)wcslen( DllName ) + 1); 271 | param = VirtualAllocEx(ppi->hProcess, NULL, len, MEM_COMMIT,PAGE_READWRITE); 272 | if (param == NULL) 273 | { 274 | DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError()); 275 | goto no_go; 276 | } 277 | WriteProcMem( param, DllName, len ); 278 | } 279 | thread = CreateRemoteThread( ppi->hProcess, NULL, 4096, 280 | (LPTHREAD_START_ROUTINE)proc, param, 0, NULL ); 281 | WaitForSingleObject( thread, INFINITE ); 282 | CloseHandle( thread ); 283 | if (!unload) 284 | VirtualFreeEx( ppi->hProcess, param, 0, MEM_RELEASE ); 285 | } 286 | 287 | 288 | DWORD CtrlHandler( DWORD event ) 289 | { 290 | return (event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT); 291 | } 292 | 293 | 294 | int main( void ) 295 | { 296 | STARTUPINFO si; 297 | PROCESS_INFORMATION pi; 298 | LPTSTR argv, arg, cmd; 299 | TCHAR buf[4]; 300 | BOOL shell, run, gui; 301 | DWORD len; 302 | int rc = 0; 303 | 304 | // Convert wide strings using the current code page. 305 | sprintf( (LPSTR)buf, ".%u", GetConsoleOutputCP() ); 306 | setlocale( LC_CTYPE, (LPSTR)buf ); 307 | 308 | // Switch console output to Unicode. 309 | if (_isatty( 1 )) 310 | _setmode( 1, _O_U16TEXT); 311 | if (_isatty( 2 )) 312 | _setmode( 2, _O_U16TEXT); 313 | 314 | // Create a console handle and store the current attributes. 315 | hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, 316 | FILE_SHARE_READ | FILE_SHARE_WRITE, 317 | NULL, OPEN_EXISTING, 0, 0 ); 318 | 319 | argv = GetCommandLine(); 320 | len = (DWORD)wcslen( argv ) + 1; 321 | if (len < MAX_PATH) 322 | len = MAX_PATH; 323 | arg = malloc( TSIZE(len) ); 324 | get_arg( arg, &argv, &cmd ); // skip the program name 325 | get_arg( arg, &argv, &cmd ); 326 | 327 | if (*arg) 328 | { 329 | if (wcscmp( arg, L"/?" ) == 0 || 330 | wcscmp( arg, L"--help" ) == 0) 331 | { 332 | help(); 333 | return rc; 334 | } 335 | if (wcscmp( arg, L"--version" ) == 0) 336 | { 337 | _putws( L"ANSICON (" BITS L"-bit) version " PVERS L" (" PDATE L")." ); 338 | return rc; 339 | } 340 | } 341 | 342 | *buf = '\0'; 343 | GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) ); 344 | log_level = _wtoi( buf ); 345 | 346 | #ifdef _WIN64 347 | if (*arg == '-' && arg[1] == 'P') 348 | { 349 | pi.dwProcessId = _wtoi( arg + 2 ); 350 | pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId ); 351 | if (pi.hProcess == NULL) 352 | { 353 | DEBUGSTR( 1, " Unable to open process %u (%u)", 354 | pi.dwProcessId, GetLastError() ); 355 | } 356 | else 357 | { 358 | PBYTE base; 359 | DEBUGSTR( 1, "64-bit process (%u) started by 32-bit", pi.dwProcessId ); 360 | if (ProcessType( &pi, &base, NULL ) == 48) 361 | RemoteLoad64( &pi ); 362 | else 363 | InjectDLL( &pi, base ); 364 | CloseHandle( pi.hProcess ); 365 | } 366 | return 0; 367 | } 368 | #endif 369 | 370 | if (log_level) 371 | DEBUGSTR( 1, NULL ); // start a new session 372 | 373 | shell = run = TRUE; 374 | 375 | while (*arg == '-') 376 | { 377 | switch (arg[1]) 378 | { 379 | case 'l': 380 | SetEnvironmentVariable( L"ANSICON_LOG", arg + 2 ); 381 | log_level = _wtoi( arg + 2 ); 382 | DEBUGSTR( 1, NULL ); // create a session 383 | break; 384 | 385 | case 'i': 386 | case 'I': 387 | case 'u': 388 | case 'U': 389 | shell = FALSE; 390 | process_autorun( arg[1] ); 391 | if (arg[1] == 'u' || arg[1] == 'U') 392 | break; 393 | // else fall through 394 | 395 | case 'p': 396 | { 397 | BOOL unload = (arg[1] == 'p' && arg[2] == 'u'); 398 | shell = FALSE; 399 | if (GetParentProcessInfo( &pi, arg )) 400 | { 401 | pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); 402 | RemoteLoad( &pi, arg, unload ); 403 | CloseHandle( pi.hProcess ); 404 | } 405 | else 406 | { 407 | fputws( L"ANSICON: could not obtain the parent process.\n", stderr ); 408 | rc = 1; 409 | } 410 | break; 411 | } 412 | 413 | case 'm': 414 | SetEnvironmentVariable( L"ANSICON_DEF", arg[2] ? arg + 2 : L"7" ); 415 | break; 416 | 417 | case 'e': 418 | case 'E': 419 | case 't': 420 | case 'T': 421 | run = FALSE; 422 | ++arg; 423 | goto arg_out; 424 | } 425 | get_arg( arg, &argv, &cmd ); 426 | } 427 | arg_out: 428 | if (run && *cmd == '\0') 429 | { 430 | if (!shell) 431 | run = FALSE; 432 | else if (!_isatty( 0 )) 433 | { 434 | *arg = 't'; 435 | run = FALSE; 436 | } 437 | } 438 | 439 | // Ensure the default attributes are the current attributes. 440 | WriteConsole( hConOut, L"\33[m", 3, &len, NULL ); 441 | 442 | if (run) 443 | { 444 | if (*cmd == '\0') 445 | { 446 | if (!GetEnvironmentVariable( L"ComSpec", arg, MAX_PATH )) 447 | { 448 | // CreateProcessW writes to the string, so can't simply point to "cmd". 449 | wcscpy( arg, L"cmd" ); 450 | } 451 | cmd = arg; 452 | } 453 | 454 | ZeroMemory( &si, sizeof(si) ); 455 | si.cb = sizeof(si); 456 | if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi )) 457 | { 458 | ProcessType( &pi, NULL, &gui ); 459 | if (!gui) 460 | { 461 | SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE ); 462 | WaitForSingleObject( pi.hProcess, INFINITE ); 463 | GetExitCodeProcess( pi.hProcess, (LPDWORD)(LPVOID)&rc ); 464 | } 465 | CloseHandle( pi.hProcess ); 466 | CloseHandle( pi.hThread ); 467 | } 468 | else 469 | { 470 | print_error( arg ); 471 | rc = 1; 472 | } 473 | } 474 | else if (*arg) 475 | { 476 | if (*arg == 'e' || *arg == 'E') 477 | { 478 | cmd += 2; 479 | if (*cmd == ' ' || *cmd == '\t') 480 | ++cmd; 481 | fputws( cmd, stdout ); 482 | if (*arg == 'e') 483 | _putws( L"" ); 484 | } 485 | else // (*arg == 't' || *arg == 'T') 486 | { 487 | BOOL title = (*arg == 'T'); 488 | get_file( arg, &argv, &cmd ); 489 | if (*arg == '\0') 490 | wcscpy( arg, L"-" ); 491 | do 492 | { 493 | if (title) 494 | { 495 | wprintf( L"==> %s <==\n", arg ); 496 | display( arg, title ); 497 | _putws( L"" ); 498 | } 499 | else 500 | display( arg, title ); 501 | get_file( arg, &argv, &cmd ); 502 | } while (*arg); 503 | } 504 | } 505 | 506 | return rc; 507 | } 508 | 509 | 510 | // Display a file. 511 | void display( LPCTSTR name, BOOL title ) 512 | { 513 | HANDLE in, out; 514 | BOOL pipe; 515 | char buf[8192]; 516 | DWORD len; 517 | 518 | if (*name == '-' && name[1] == '\0') 519 | { 520 | pipe = TRUE; 521 | in = GetStdHandle( STD_INPUT_HANDLE ); 522 | } 523 | else 524 | { 525 | pipe = FALSE; 526 | in = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 527 | NULL, OPEN_EXISTING, 0, NULL ); 528 | if (in == INVALID_HANDLE_VALUE) 529 | { 530 | print_error( name ); 531 | return; 532 | } 533 | } 534 | if (title) 535 | { 536 | _putws( L"" ); 537 | // Need to flush, otherwise it's written *after* STD_OUTPUT_HANDLE should 538 | // it be redirected. 539 | fflush( stdout ); 540 | } 541 | out = GetStdHandle( STD_OUTPUT_HANDLE ); 542 | for (;;) 543 | { 544 | if (!ReadFile( in, buf, sizeof(buf), &len, NULL )) 545 | { 546 | if (GetLastError() != ERROR_BROKEN_PIPE) 547 | print_error( name ); 548 | break; 549 | } 550 | if (len == 0) 551 | break; 552 | WriteFile( out, buf, len, &len, NULL ); 553 | } 554 | if (!pipe) 555 | CloseHandle( in ); 556 | } 557 | 558 | 559 | void print_error( LPCTSTR name ) 560 | { 561 | LPTSTR errmsg = NULL; 562 | DWORD err = GetLastError(); 563 | 564 | fputws( L"ANSICON: ", stderr ); 565 | if (err == ERROR_BAD_EXE_FORMAT) 566 | { 567 | // This error requires an argument, which is name. 568 | FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | 569 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 570 | FORMAT_MESSAGE_ARGUMENT_ARRAY, 571 | NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, (va_list*)&name ); 572 | fputws( errmsg, stderr ); 573 | } 574 | else 575 | { 576 | if (FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | 577 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 578 | FORMAT_MESSAGE_IGNORE_INSERTS, 579 | NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL )) 580 | fwprintf( stderr, L"%s: %s", name, errmsg ); 581 | else 582 | fwprintf( stderr, L"%s: Error %lu.\n", name, err ); 583 | } 584 | LocalFree( errmsg ); 585 | } 586 | 587 | 588 | // Add or remove ANSICON to AutoRun. 589 | void process_autorun( TCHAR cmd ) 590 | { 591 | HKEY cmdkey; 592 | TCHAR ansicon[MAX_PATH+80]; 593 | TCHAR logstr[80]; 594 | LPTSTR autorun, ansirun; 595 | DWORD len, type, exist; 596 | BOOL inst; 597 | 598 | if (log_level) 599 | _snwprintf( logstr, lenof(logstr), L"set ANSICON_LOG=%d&", log_level ); 600 | else 601 | *logstr = '\0'; 602 | len = TSIZE(_snwprintf( ansicon, lenof(ansicon), 603 | L"(if %%ANSICON_VER%%==^%%ANSICON_VER^%% %s\"%s\" -p)", 604 | logstr, prog_path ) + 1); 605 | 606 | inst = (towlower( cmd ) == 'i'); 607 | if (RegCreateKeyEx( (iswlower(cmd)) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, 608 | CMDKEY, 0, NULL, 609 | REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 610 | &cmdkey, &exist ) != ERROR_SUCCESS) 611 | { 612 | fputws( L"ANSICON: could not update AutoRun", stderr ); 613 | if (iswupper( cmd )) 614 | fwprintf( stderr, L" (perhaps use -%c, or run as admin)", towlower(cmd) ); 615 | fputws( L".\n", stderr ); 616 | } 617 | exist = 0; 618 | RegQueryValueEx( cmdkey, AUTORUN, NULL, NULL, NULL, &exist ); 619 | if (exist == 0) 620 | { 621 | if (inst) 622 | RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); 623 | } 624 | else 625 | { 626 | // Let's assume there's sufficient memory. 627 | autorun = malloc( exist + len ); 628 | RegQueryValueEx( cmdkey, AUTORUN, NULL, &type, (PBYTE)autorun, &exist ); 629 | // Remove the existing command, if present. 630 | ansirun = wcsstr( autorun, L"(if %ANSICON_VER%" ); 631 | if (ansirun != NULL) 632 | { 633 | LPTSTR tmp = wcschr( ansirun, '"' ); // opening quote 634 | tmp = wcschr( tmp + 1, '"' ); // closing quote 635 | tmp = wcschr( tmp + 1, ')' ); // closing bracket 636 | if (*++tmp == '&') 637 | ++tmp; 638 | if (*tmp == '&') 639 | ++tmp; 640 | if (*tmp == '\0') 641 | { 642 | if (ansirun > autorun && ansirun[-1] == '&') 643 | --ansirun; 644 | if (ansirun > autorun && ansirun[-1] == '&') 645 | --ansirun; 646 | } 647 | wcscpy( ansirun, tmp ); 648 | exist = TSIZE((DWORD)wcslen( autorun ) + 1); 649 | } 650 | if (inst) 651 | { 652 | if (exist == sizeof(TCHAR)) 653 | RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len ); 654 | else 655 | { 656 | memmove( (PBYTE)autorun + len, autorun, exist ); 657 | memcpy( autorun, ansicon, len ); 658 | ((PBYTE)autorun)[len-sizeof(TCHAR)] = '&'; 659 | RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist+len ); 660 | } 661 | } 662 | else 663 | { 664 | if (exist == sizeof(TCHAR)) 665 | RegDeleteValue( cmdkey, AUTORUN ); 666 | else 667 | RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist ); 668 | } 669 | free( autorun ); 670 | } 671 | RegCloseKey( cmdkey ); 672 | } 673 | 674 | 675 | // Search each process in the snapshot for id. 676 | BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe ) 677 | { 678 | BOOL fOk; 679 | 680 | ppe->dwSize = sizeof(PROCESSENTRY32); 681 | for (fOk = Process32First( snap, ppe ); fOk; fOk = Process32Next( snap, ppe )) 682 | if (ppe->th32ProcessID == id) 683 | break; 684 | 685 | return fOk; 686 | } 687 | 688 | 689 | // Obtain the process identifier of the parent process. 690 | BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name ) 691 | { 692 | HANDLE hSnap; 693 | PROCESSENTRY32 pe; 694 | BOOL fOk; 695 | 696 | hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); 697 | if (hSnap == INVALID_HANDLE_VALUE) 698 | { 699 | DEBUGSTR( 1, "Failed to create snapshot (%u)", GetLastError() ); 700 | return FALSE; 701 | } 702 | 703 | fOk = (find_proc_id( hSnap, GetCurrentProcessId(), &pe ) && 704 | find_proc_id( hSnap, pe.th32ParentProcessID, &pe )); 705 | CloseHandle( hSnap ); 706 | if (!fOk) 707 | { 708 | DEBUGSTR( 1, "Failed to locate parent" ); 709 | return FALSE; 710 | } 711 | 712 | ppi->dwProcessId = pe.th32ProcessID; 713 | wcscpy( name, pe.szExeFile ); 714 | 715 | return fOk; 716 | } 717 | 718 | 719 | // Return the first non-space character from arg. 720 | LPTSTR skip_spaces( LPTSTR arg ) 721 | { 722 | while (*arg == ' ' || *arg == '\t') 723 | ++arg; 724 | 725 | return arg; 726 | } 727 | 728 | 729 | // Retrieve an argument from the command line. cmd gets the existing argv; argv 730 | // is ready for the next argument. 731 | void get_arg( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd ) 732 | { 733 | LPTSTR line; 734 | 735 | line = *cmd = skip_spaces( *argv ); 736 | while (*line != '\0') 737 | { 738 | if (*line == ' ' || *line == '\t') 739 | { 740 | ++line; 741 | break; 742 | } 743 | if (*line == '"') 744 | { 745 | while (*++line != '\0') 746 | { 747 | if (*line == '"') 748 | { 749 | ++line; 750 | break; 751 | } 752 | *arg++ = *line; 753 | } 754 | } 755 | else 756 | *arg++ = *line++; 757 | } 758 | *arg = '\0'; 759 | *argv = line; 760 | } 761 | 762 | 763 | int glob_sort( const void* a, const void* b ) 764 | { 765 | return lstrcmpi( *(LPCTSTR*)a, *(LPCTSTR*)b ); 766 | } 767 | 768 | 769 | // As get_arg, but expand wildcards. 770 | void get_file( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd ) 771 | { 772 | HANDLE fh, in; 773 | WIN32_FIND_DATA fd; 774 | LPTSTR path; 775 | int size; 776 | char buf[1024]; 777 | static LPTSTR name; 778 | static LPTSTR* glob; 779 | static int globbed; 780 | 781 | if (globbed != 0) 782 | { 783 | if (glob[globbed] == NULL) 784 | { 785 | free( glob ); 786 | globbed = 0; 787 | } 788 | else 789 | { 790 | wcscpy( name, glob[globbed++] ); 791 | return; 792 | } 793 | } 794 | 795 | get_arg( arg, argv, cmd ); 796 | if (wcspbrk( arg, L"*?" ) != NULL) 797 | { 798 | fh = FindFirstFile( arg, &fd ); 799 | if (fh != INVALID_HANDLE_VALUE) 800 | { 801 | size = 0; 802 | do 803 | { 804 | if (! (fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | 805 | FILE_ATTRIBUTE_HIDDEN))) 806 | { 807 | ++globbed; 808 | size += (int)wcslen( fd.cFileName ) + 1; 809 | } 810 | } while (FindNextFile( fh, &fd )); 811 | FindClose( fh ); 812 | 813 | if (globbed != 0) 814 | { 815 | for (path = name = arg; *path != '\0'; ++path) 816 | if (*path == '\\' || *path == '/') 817 | name = path + 1; 818 | glob = malloc( (globbed + 1) * PTRSZ + TSIZE(size) ); 819 | path = (LPTSTR)(glob + globbed + 1); 820 | globbed = 0; 821 | fh = FindFirstFile( arg, &fd ); 822 | do 823 | { 824 | if (! (fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | 825 | FILE_ATTRIBUTE_HIDDEN))) 826 | { 827 | // Ignore apparent binary files. 828 | wcscpy( name, fd.cFileName ); 829 | in = CreateFile( arg, GENERIC_READ, 830 | FILE_SHARE_READ|FILE_SHARE_WRITE, 831 | NULL, OPEN_EXISTING, 0, NULL ); 832 | if (in != INVALID_HANDLE_VALUE) 833 | { 834 | ReadFile( in, buf, sizeof(buf), (LPVOID)&size, NULL ); 835 | CloseHandle( in ); 836 | if (memchr( buf, 0, size ) != NULL) 837 | continue; 838 | } 839 | size = (int)wcslen( fd.cFileName ) + 1; 840 | memcpy( path, fd.cFileName, TSIZE(size) ); 841 | glob[globbed++] = path; 842 | path += size; 843 | } 844 | } while (FindNextFile( fh, &fd )); 845 | FindClose( fh ); 846 | glob[globbed] = NULL; 847 | 848 | qsort( glob, globbed, PTRSZ, glob_sort ); 849 | 850 | wcscpy( name, glob[0] ); 851 | globbed = 1; 852 | } 853 | } 854 | } 855 | } 856 | 857 | 858 | // VC macros don't like preprocessor statements mixed with strings. 859 | #ifdef _WIN64 860 | #define WINTYPE L"Windows" 861 | #else 862 | #define WINTYPE L"Win32" 863 | #endif 864 | 865 | void help( void ) 866 | { 867 | _putws( 868 | L"ANSICON by Jason Hood .\n" 869 | L"Version " PVERS L" (" PDATE L"). Freeware.\n" 870 | L"http://ansicon.adoxa.vze.com/\n" 871 | L"\n" 872 | L"Process ANSI escape sequences in " WINTYPE L" console programs.\n" 873 | L"\n" 874 | L"ansicon [-lLEVEL] [-i] [-I] [-u] [-U] [-m[ATTR]] [-p[u]]\n" 875 | L" [-e|E STRING | -t|T [FILE...] | PROGRAM [ARGS]]\n" 876 | L"\n" 877 | L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n" 878 | L" \t\t +4=output, +8=append, +16=imports, +32=files) for PROGRAM\n" 879 | L" -i\t\tinstall - add ANSICON to CMD's AutoRun entry (also implies -p)\n" 880 | L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n" 881 | L" -I -U\t\tuse local machine instead of current user\n" 882 | L" -m\t\tuse grey on black (\"monochrome\") or ATTR as default color\n" 883 | L" -p\t\thook into the parent process\n" 884 | L" -pu\t\tunhook from the parent process\n" 885 | L" -e\t\techo STRING\n" 886 | L" -E\t\techo STRING, don't append newline\n" 887 | L" -t\t\tdisplay files (\"-\" for stdin), combined as a single stream\n" 888 | L" -T\t\tdisplay files, name first, blank line before and after\n" 889 | L" PROGRAM\trun the specified program\n" 890 | L" nothing\trun a new command processor, or display stdin if redirected\n" 891 | L"\n" 892 | L"ATTR is one or two hexadecimal digits; please use \"COLOR /?\" for details.\n" 893 | L"It may start with '-' to reverse foreground and background (but not for -p)." 894 | ); 895 | } 896 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | 2 | ANSICON 3 | 4 | Copyright 2005-2019 Jason Hood 5 | 6 | Version 1.89. Freeware 7 | 8 | 9 | Description 10 | =========== 11 | 12 | ANSICON provides ANSI escape sequences for Windows console programs. It 13 | provides much the same functionality as 'ANSI.SYS' does for MS-DOS. 14 | 15 | 16 | Requirements 17 | ============ 18 | 19 | 32-bit: Windows 2000 Professional and later (it won't work with NT or 9X). 20 | 64-bit: AMD64 (IA64 could work with a little modification). 21 | 22 | 23 | Installation 24 | ============ 25 | 26 | There are three ways to install, depending on your usage. 27 | 28 | * Add "x86" (if your OS is 32-bit) or "x64" (if 64-bit) to your PATH, or 29 | copy the relevant files to a directory already on the PATH (but NOT to 30 | "System32" on a 64-bit system). This means you explicitly run 'ansicon' 31 | whenever you want to use it. 32 | 33 | * Use option '-i' (or '-I', if permitted) to add an entry to CMD.EXE's 34 | AutoRun registry value (current user or local machine, respectively). 35 | This means "Command Prompt" and any program started by CMD.EXE will 36 | automatically have sequences. 37 | 38 | * Add "d:\path\to\ansicon.exe -p" to your Startup group (run minimized to 39 | avoid the console window flashing). This means any console program 40 | started by Explorer will automatically have sequences. 41 | 42 | Uninstall involves closing any programs that are currently using it; using 43 | the Run dialog to run "d:\path\to\ansicon.exe -pu" to remove it from 44 | Explorer; running with '-u' (and/or '-U') to remove it from AutoRun; remov- 45 | ing the directory from PATH; and deleting the files. No other changes are 46 | made (unless you created environment variables). 47 | 48 | Upgrading 49 | --------- 50 | 51 | Delete ANSICON_API - it has switched from WriteFile to WriteConsoleA. 52 | 53 | 54 | Usage 55 | ===== 56 | 57 | Options (case sensitive): 58 | 59 | -l Log to "%TEMP%\ansicon.log". 60 | 61 | -p Enable the parent process (i.e. the command shell used to run 62 | ANSICON) to recognise escapes. 63 | 64 | -pu Unload from the parent process, restoring it. 65 | 66 | -m Set the current (and default) attribute to grey on black 67 | ("monochrome"), or the attribute following the 'm' (please 68 | use 'COLOR /?' for attribute values). 69 | 70 | -e Echo the command line - a space or tab after the 'e' is 71 | ignored, the remainder is displayed verbatim. 72 | 73 | -E As above, but no newline is added. 74 | 75 | -t Display ("type") each file (or standard input if none or the 76 | name is "-") as though they are a single file. 77 | 78 | -T Display "==> FILE NAME <==", a blank line (or an error 79 | message), the file and another blank line. 80 | 81 | Running ANSICON with no arguments will start a new instance of the command 82 | processor (the program defined by the 'ComSpec' environment variable, typ- 83 | ically 'CMD.EXE'), or display standard input if it is redirected. Any arg- 84 | ument will be treated as a program and its arguments. 85 | 86 | E.g.: 'ansicon -m30 -t file.ans' will display "file.ans" using black on 87 | cyan as the default color. 88 | 89 | The attribute may start with '-' to permanently reverse the foreground and 90 | background colors (but not when using '-p'). E.g.: 'ansicon -m-f0 -t 91 | file.log' will use reversed black on white as the default (i.e. white on 92 | black, with foreground sequences changing the background). 93 | 94 | If you experience trouble with certain programs, the log may help in find- 95 | ing the cause; it can be found at "%TEMP%\ansicon.log". A number should 96 | immediately follow the 'l': 97 | 98 | 0 No logging 99 | 1 Log process start and end 100 | 2 Above, plus log modules used by the process 101 | 3 Above, plus log functions that are hooked 102 | 4 Log console output (add to any of the above) 103 | 8 Append to the existing file (add to any of the above) 104 | 16 Log all imported modules (add to any of the above) 105 | 32 Log CreateFile (add to any of the above) 106 | 107 | The log option will not work with '-p'; set the environment variable 108 | ANSICON_LOG (to the number) instead. The variable is only read once when a 109 | process is started; changing it won't affect running processes. If you 110 | identify a program or module that causes problems, add it to the 111 | ANSICON_EXC environment variable (see ANSICON_API below; add the extension 112 | to exclude a single module). Be aware that excluding a program will also 113 | exclude any programs it creates (alghough excluding "program.exe" may still 114 | hook created programs run through its DLLs). 115 | 116 | E.g.: 'ansicon -l5' will start a new command processor, logging every pro- 117 | cess it starts along with their output. 118 | 119 | Once installed, the ANSICON environment variable will be created. This 120 | variable is of the form "WxH (wxh)", where 'W' & 'H' are the width and 121 | height of the buffer and 'w' & 'h' are the width and height of the window. 122 | The variable is updated whenever a program reads it directly (i.e. as an 123 | individual request, not as part of the entire environment block). For 124 | example, 'set an' will not update it, but 'echo %ansicon%' will. Also 125 | created are ANSICON_VER, which contains the version without the point (1.80 126 | becomes "180"), and CLICOLOR (see http://bixense.com/clicolors/), which 127 | contains "1". These variables do not exist as part of the environment 128 | block (e.g. 'set an' will not show ANSICON_VER). 129 | 130 | If installed, GUI programs will not be hooked. Either start the program 131 | directly with 'ansicon', or add it to the ANSICON_GUI variable (see 132 | ANSICON_API below). 133 | 134 | Using 'ansicon' after install will always start with the default attrib- 135 | utes, restoring the originals on exit; all other programs will use the cur- 136 | rent attributes. The shift state and insert mode are always reset for a 137 | new process. 138 | 139 | ANSICON will detect when a line wraps at the right margin and suppress a 140 | following newline. Some programs detect the wrap themselves and so the 141 | following newline is actually for a blank line; use the ANSICON_WRAP 142 | variable to indicate as such (see ANSICON_API below). 143 | 144 | My version of WriteConsoleA will always set the number of characters writt- 145 | en, not the number of bytes. This means writing a double-byte character as 146 | two bytes will set 0 the first write (nothing was written) and 1 the second 147 | (when the character was actually written); Windows normally sets 1 for both 148 | writes. Similarly, writing the individual bytes of a multibyte character 149 | will set 0 for all but the last byte, then 1 on the last; Windows normally 150 | sets 1 for each byte, writing the undefined character. However, my 151 | WriteFile (and _lwrite/_hwrite) will always set what was received; Windows, 152 | using a multibyte character set (but not DBCS), would set the characters. 153 | You can have WriteConsoleA return the original byte count by using the 154 | ANSICON_API environment variable: 155 | 156 | ANSICON_API=[!]program;program;program... 157 | 158 | PROGRAM is the name of the program, with no path and extension. The lead- 159 | ing exclamation inverts the usage, meaning the API will always be over- 160 | ridden, unless the program is in the list. The variable can be made perm- 161 | anent by going to System Properties, selecting the Advanced tab (with Vista 162 | onwards, this can be done by running "SystemPropertiesAdvanced") and click- 163 | ing Environment Variables. 164 | 165 | 166 | Sequences Recognised 167 | ==================== 168 | 169 | The following escape sequences are recognised (see "sequences.txt" for a 170 | more complete description). 171 | 172 | \e]0;titleBEL xterm: Set window's title (and icon, ignored) 173 | \e]2;titleBEL xterm: Set window's title 174 | \e]4;...BEL xterm: Change color(s) 175 | \e]104;...BEL xterm: Reset color(s) 176 | \e[21t xterm: Report window's title 177 | \e[s ANSI.SYS: Save Cursor Position 178 | \e[u ANSI.SYS: Restore Cursor Position 179 | \e[1+h ACFM Flush Mode (flush immediately) 180 | \e[1+l ACFM Flush Mode (flush when necessary) 181 | BEL BEL Bell 182 | \e[#Z CBT Cursor Backward Tabulation 183 | \e[#G CHA Cursor Character Absolute 184 | \e[#I CHT Cursor Forward Tabulation 185 | \e[#E CNL Cursor Next Line 186 | \e[#F CPL Cursor Preceding Line 187 | \e[3h CRM Control Representation Mode (display controls) 188 | \e[3l CRM Control Representation Mode (perform controls) 189 | \e[#D CUB Cursor Left 190 | \e[#B CUD Cursor Down 191 | \e[#C CUF Cursor Right 192 | \e[#;#H CUP Cursor Position 193 | \e[#A CUU Cursor Up 194 | \e[c DA Device Attributes 195 | \e[#P DCH Delete Character 196 | \e[?7h DECAWM Autowrap Mode (autowrap) 197 | \e[?7l DECAWM Autowrap Mode (no autowrap) 198 | \e[?3h DECCOLM Selecting 80 or 132 Columns per Page (132) 199 | \e[?3l DECCOLM Selecting 80 or 132 Columns per Page (prior) 200 | \e[?95h DECNCSM No Clearing Screen On Column Change Mode (keep) 201 | \e[?95l DECNCSM No Clearing Screen On Column Change Mode (clear) 202 | \e[?6h DECOM Origin Mode (top margin) 203 | \e[?6l DECOM Origin Mode (top line) 204 | \e[#;#;#...,~ DECPS Play Sound 205 | \e8 DECRC Restore Cursor 206 | \e7 DECSC Save Cursor 207 | \e[?5W DECST8C Set Tab at Every 8 Columns 208 | \e[?5;#W DECST8C Set Tab at Every # Columns (ANSICON extension) 209 | \e[#;#r DECSTBM Set Top and Bottom Margins 210 | \e[!p DECSTR Soft Terminal Reset 211 | \e[?25h DECTCEM Text Cursor Enable Mode (show cursor) 212 | \e[?25l DECTCEM Text Cursor Enable Mode (hide cursor) 213 | \e[#M DL Delete Line 214 | \e[#n DSR Device Status Report 215 | \e[#X ECH Erase Character 216 | \e[#J ED Erase In Page 217 | \e[#K EL Erase In Line 218 | \e[#` HPA Character Position Absolute 219 | \e[#j HPB Character Position Backward 220 | \e[#a HPR Character Position Forward 221 | HT HT Character Tabulation 222 | \eH HTS Character Tabulation Set 223 | \e[#;#f HVP Character And Line Position 224 | \e[#@ ICH Insert Character 225 | \e[#L IL Insert Line 226 | \eD IND Index 227 | \e[4h IRM Insertion Replacement Mode (insert) 228 | \e[4l IRM Insertion Replacement Mode (replace) 229 | SI LS0 Locking-shift Zero (see below) 230 | SO LS1 Locking-shift One 231 | \eE NEL Next Line 232 | \e[#b REP Repeat 233 | \eM RI Reverse Index 234 | \ec RIS Reset to Initial State 235 | \e(0 SCS Select Character Set (DEC special graphics) 236 | \e(B SCS Select Character Set (ASCII) 237 | \e[#;#;#m SGR Select Graphic Rendition 238 | \e[#T SD Scroll Down/Pan Up 239 | \e[#S SU Scroll Up/Pan Down 240 | \e[#g TBC Tabulation Clear 241 | \e[#d VPA Line Position Absolute 242 | \e[#k VPB Line Position Backward 243 | \e[#e VPR Line Position Forward 244 | 245 | '\e' represents the escape character (ASCII 27); '#' represents a decimal 246 | number (optional, in most cases defaulting to 1); BEL, HT, SO and SI are 247 | ASCII 7, 9, 14 and 15. 248 | 249 | Escape followed by a control character will display that character, not 250 | perform its function; an unrecognised character will preserve the escape. 251 | 252 | SO will select the G1 character set; SI will select the G0 set. The G0 253 | character set is set by SCS; the G1 character set is always the DEC Special 254 | Graphics Character Set. 255 | 256 | I make a distinction between '\e[m' and '\e[0;...m'. Both will restore the 257 | original foreground/background colors (and so '0' should be the first para- 258 | meter); the former will also restore the original bold and underline attri- 259 | butes, whilst the latter will explicitly reset them. The environment var- 260 | iable ANSICON_DEF can be used to change the default colors (same value as 261 | '-m'; setting the variable does not change the current colors). 262 | 263 | The first time a program clears the screen ('\e[2J') will actually scroll 264 | in a new window (assuming the buffer is bigger than the window, of course). 265 | Subsequent clears will then blank the window. However, if the window has 266 | scrolled, or the cursor is on the last line of the buffer, it will again 267 | scroll in a new window. 268 | 269 | 270 | DEC Special Graphics Character Set 271 | ================================== 272 | 273 | This is my interpretation of the set, as shown by 274 | http://vt100.net/docs/vt220-rm/table2-4.html. 275 | 276 | 277 | Char Unicode Code Point & Name 278 | ---- ------------------------- 279 | _ U+00A0 No-Break Space 280 | ` U+2666 Black Diamond Suit 281 | a U+2592 Medium Shade 282 | b U+2409 Symbol For Horizontal Tabulation 283 | c U+240C Symbol For Form Feed 284 | d U+240D Symbol For Carriage Return 285 | e U+240A Symbol For Line Feed 286 | f U+00B0 Degree Sign 287 | g U+00B1 Plus-Minus Sign 288 | h U+2424 Symbol For Newline 289 | i U+240B Symbol For Vertical Tabulation 290 | j U+2518 Box Drawings Light Up And Left 291 | k U+2510 Box Drawings Light Down And Left 292 | l U+250C Box Drawings Light Down And Right 293 | m U+2514 Box Drawings Light Up And Right 294 | n U+253C Box Drawings Light Vertical And Horizontal 295 | o U+23BA Horizontal Scan Line-1 296 | p U+23BB Horizontal Scan Line-3 297 | q U+2500 Box Drawings Light Horizontal (SCAN 5) 298 | r U+23BC Horizontal Scan Line-7 299 | s U+23BD Horizontal Scan Line-9 300 | t U+251C Box Drawings Light Vertical And Right 301 | u U+2524 Box Drawings Light Vertical And Left 302 | v U+2534 Box Drawings Light Up And Horizontal 303 | w U+252C Box Drawings Light Down And Horizontal 304 | x U+2502 Box Drawings Light Vertical 305 | y U+2264 Less-Than Or Equal To 306 | z U+2265 Greater-Than Or Equal To 307 | { U+03C0 Greek Small Letter Pi 308 | | U+2260 Not Equal To 309 | } U+00A3 Pound Sign 310 | ~ U+00B7 Middle Dot 311 | 312 | G1.txt is a Unicode file to view the glyphs "externally". G1.bat is a 313 | batch file (using 'x86\ansicon') to show the glyphs in the console. The 314 | characters will appear as they should using Lucida (other than the Sym- 315 | bols), but code page will influence them when using a raster font (but of 316 | particular interest, 437 and 850 both show the Box Drawings). 317 | 318 | 319 | Limitations 320 | =========== 321 | 322 | Tabs can only be set up to column 2048. 323 | The saved position will not be restored correctly if the buffer scrolls. 324 | Palette sequences only work from Vista. 325 | 326 | There may be a conflict with NVIDIA's drivers, requiring the setting of the 327 | Environment Variable: 328 | 329 | ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll 330 | 331 | An application using multiple screen buffers will not have separate 332 | attributes in each buffer. 333 | 334 | Console input that is echoed will not be processed (which is probably a 335 | good thing for escapes, but not so good for margins). 336 | 337 | 338 | Version History 339 | =============== 340 | 341 | Legend: + added, - bug-fixed, * changed. 342 | 343 | 1.89 - 29 April, 2019: 344 | - fix occasional freeze on startup (bug converting 8-digit window handle). 345 | 346 | 1.88 - 1 March, 2019: 347 | - fix ANSICON environment variable when there is no console. 348 | 349 | 1.87 - 3 February, 2019: 350 | - fix crash when some programs start (bug during hooking); 351 | - properly hook SetCurrentConsoleFontEx. 352 | 353 | 1.86 - 4 November, 2018: 354 | - check the DLL exists before importing it (allows renaming to update); 355 | - unhook on terminate, as well (fixes issues with Vista and MinGW). 356 | 357 | 1.85 - 23 August, 2018: 358 | - fix wrap issues with a buffer bigger than the window; 359 | - fix -e et al when redirecting to NUL; 360 | - prevent -p from injecting when already injected; 361 | - fix running directly via ansicon (hook even if it's GUI or excluded); 362 | - preserve last error; 363 | + add log level 32 to monitor CreateFile. 364 | 365 | 1.84 - 11 May, 2018: 366 | - close the flush handles on detach; 367 | - WriteFile wasn't properly testing if its handle was for a console; 368 | - use remote load on Win8+ if the process has no IAT; 369 | - fix logging really long command lines; 370 | - default to 7 or -7 if ANSICON_DEF could not be parsed; 371 | - workaround for a Windows 10 1803 console bug (doubled CMD prompt); 372 | * remove dependency on CRT & USER32, dynamically load WINMM; 373 | * exit process if the primary thread is detached (for processes on Win10 374 | that return, rather than call ExitProcess); 375 | * ansicon.exe statically loads the DLL; 376 | * scrolling will use the default attribute for new lines. 377 | 378 | 1.83 - 16 February, 2018: 379 | - create the flush thread on first use. 380 | 381 | 1.82 - 13 February, 2018: 382 | - add ANSICON_WRAP for programs that expect the wrap at right margin; 383 | - make IsConsoleHandle a critical section, for multithreaded processes; 384 | - use APIConsole for all console functions (Windows 10). 385 | 386 | 1.81 - 28 December, 2017: 387 | - fix multiple CRs before LF (including preventing an immediate flush); 388 | - fix CR, BS and partial RM during CRM; 389 | - fix buffer overflow caused by incorrect critical section; 390 | * support the entire 256-color palette; 391 | * setting color by index or RGB will use the nearest console color; 392 | * setting color by index will leave bold/underline unchanged. 393 | 394 | 1.80 - 24 December, 2017: 395 | - fix unloading; 396 | - fix -e et al when redirecting to CON; 397 | - hook CreateFile and CreateConsoleScreenBuffer to force read/write access 398 | (fixes redirecting to CON and Microsoft's conio); 399 | - fix cursor report with duplicated digits (e.g. "11" was only writing "1"); 400 | - fix issues with CRM; 401 | - fix explicit zero parameters not defaulting to 1; 402 | - set color by index (also setting bold/underline); 403 | - fix processes that start without a window; 404 | - hide the cursor when moving (prevent it displaying on the active buffer 405 | when moving on another); 406 | * use the system default sound for the bell; 407 | * limit parameters to a maximum value of 32767; 408 | * go back to saving the buffer cursor position; 409 | * preserve escape that isn't part of a sequence; 410 | * escaped control characters will display the control; 411 | * change the graphics SCAN characters to their Unicode equivalents; 412 | * BS/CR/CUB/HVP after wrap will move back to the previous line(s); 413 | * improve speed by only flushing when necessary, adding a mode to restore 414 | flushing immediately; 415 | + added DA, DECCOLM, DECNCSM, DECOM, DECPS, DECRC, DECSC, DECST8C, DECSTBM, 416 | DECSTR, HT, HTS, IND, IRM, NEL, RI, RIS, SCS (only G0 as Special/ASCII), 417 | SD, SU and TBC; 418 | + added '+' intermediate byte to use the buffer, rather than the window; 419 | + added set/get palette sequences; 420 | + added the bright SGR colors; 421 | + added -pu to unload from the parent. 422 | 423 | 1.72 - 24 December, 2015: 424 | - handle STD_OUTPUT_HANDLE & STD_ERROR_HANDLE in WriteFile; 425 | - better handling of unusual PE files; 426 | * cache GetConsoleMode for an improvement in speed; 427 | * files writing to the console always succeed (should mostly remove the 428 | need for ANSICON_API); 429 | * log: add a blank line between processes; 430 | remove the separate line for WriteFile & _lwrite; 431 | write byte strings as-is, wide strings using the current code page; 432 | use caret notation for control characters, with hexadecimal "^xNN" 433 | and "^uNNNN" for characters not in the code page (custom printf); 434 | * join multibyte characters split across separate writes; 435 | * remove wcstok, avoiding potential interference with the host program; 436 | * similarly, remove malloc & friends, using a private heap; 437 | + add CLICOLOR dynamic environment variable. 438 | 439 | 1.71 - 23 October, 2015: 440 | + add _CRT_NON_CONFORMING_WCSTOK define for VS2015. 441 | 442 | 1.70 - 26 February, 2014: 443 | - don't hook again if using LoadLibrary or LoadLibraryEx; 444 | - update the LoadLibraryEx flags that shouldn't hook; 445 | - restore original attributes on detach (for LoadLibrary/FreeLibrary usage); 446 | - ansicon.exe will start with ANSICON_DEF (if defined and -m not used); 447 | - an installed ansicon.exe will restore current (not default) attributes; 448 | - attributes and saved position are local to each console window; 449 | - improved recognition of unsupported sequences; 450 | - restore cursor to bounds, if size reduced; 451 | - stop \e[K from erasing first character of next line; 452 | - restore cursor visibility on unload; 453 | * inject into a created process by modifying the import descriptor table 454 | (-p will use CreateRemoteThread); 455 | * log: remove the quotes around the CreateProcess command line; 456 | add an underscore in 64-bit addresses to distinguish 8-digit groups; 457 | * ANSICON_EXC can exclude entire programs; 458 | * switch G1 blank from space (U+0020) to No-Break Space (U+00A0); 459 | * use window height, not buffer; 460 | * remove newline after wrap; 461 | + recognise more sequences. 462 | 463 | 1.66 - 20 September, 2013: 464 | - fix 32-bit process trying to detect 64-bit process. 465 | 466 | 1.65 - 4 September, 2013: 467 | - fix finding 32-bit LoadLibraryW address from 64-bit; 468 | - fix \e[K (was using window, not buffer). 469 | 470 | 1.64 - 2 August, 2013: 471 | - improved detection of console output. 472 | 473 | 1.63 - 25 July, 2013: 474 | - don't write the reset sequence (when it's already installed) if output is 475 | redirected. 476 | 477 | 1.62 - 18 July, 2013: 478 | - indicate if opening HKLM failed; 479 | * removed ANSI-LLW.exe again, properly this time; 480 | * add the architecture (32- or 64-bit) to the log. 481 | 482 | 1.61 - 14 February, 2013: 483 | * revert back to using ANSI-LLW.exe, as the new method was unreliable. 484 | 485 | 1.60 - 24 November, 2012: 486 | * new method to get the 32-bit LoadLibraryW address from 64-bit code. 487 | This removes the need for ANSI-LLW.exe, which caused lots of virus 488 | warnings, for some reason. 489 | - set the code page to display some file names properly; 490 | + expand wildcards for -t (ignoring directories and hidden/binary files). 491 | 492 | 1.53 - 12 June, 2012: 493 | - fix for multiple simultaneous process creation (e.g. "cl /MP ..."). 494 | 495 | 1.52 - 2 June, 2012: 496 | + 32-bit processes can inject into 64-bit processes; 497 | + implemented \e[39m & \e[49m (set default foreground/background color); 498 | + added \e[#X, \e[#`, \e[#a, \e[#d, \e[#e, \[e#j and \e[#k; 499 | * changed sequence descriptions to those in ECMA-48, ordered by acronym. 500 | 501 | 1.51 - 24 February, 2012: 502 | - fixed installing into a piped/redirected CMD.EXE; 503 | - fixed 32-bit process trying to identify a 64-bit process; 504 | - ignore version within core API DLL names (now Win8 works); 505 | + hook _lwrite & _hwrite (now Silverfrost FTN95 v6.20 works). 506 | 507 | 1.50 - 14 December, 2011: 508 | - -u does not imply -p; 509 | - return the program's exit code; 510 | - -p by itself will not restore original color; 511 | - output error messages to stderr; 512 | * logging is always available, with various levels; include the pid; 513 | * don't automatically hook GUI programs, use 'ansicon' or ANSICON_GUI; 514 | * always place first in AutoRun; don't run if already installed; 515 | + global reverse video capability; 516 | + added ANSICON_VER to provide version/install test; 517 | + added ANSICON_EXC to exclude selected modules; 518 | + added ANSICON_DEF to explicitly set the default SGM. 519 | 520 | 1.40 - 1 March, 2011: 521 | - hook GetProcAddress (now PowerShell works); 522 | + add SO/SI, using the DEC Special Graphics Character Set for G1; 523 | + add DECTCEM to show/hide the cursor. 524 | 525 | 1.32 - 22 December, 2010: 526 | - fixed crash due to NULL lpNumberOfBytesWritten/lpNumberOfCharsWritten; 527 | - -p will test the parent process for validity; 528 | * hook into GUI processes; 529 | + recognise DSR and xterm window title sequences; 530 | - fixed MinGW32 binaries (LLW was wrong). 531 | 532 | 1.31 - 19 November, 2010: 533 | - fixed multibyte support (no extra junk with UTF-8 files); 534 | * provide workaround for API byte/character differences; 535 | * fixed potential problem if install path uses Unicode. 536 | 537 | 1.30 - 7 September, 2010: 538 | + x64 version. 539 | 540 | 1.25 - 22 July, 2010: 541 | - hook LoadLibraryEx (now CScript works); 542 | - fixed -i when AutoRun existed, but was empty; 543 | + support for Windows 7; 544 | + -I (and -U) use HKEY_LOCAL_MACHINE. 545 | 546 | 1.24 - 7 January, 2010: 547 | - fix -t and -e when ANSICON was already running; 548 | + read standard input if redirected with no arguments, if -t has no 549 | files, or if the name is "-" (which also serves as a workaround for 550 | programs that don't get hooked, such as CScript). 551 | 552 | 1.23 - 11 November, 2009: 553 | - restore hooked functions when unloading; 554 | - reverse the "bold" and "underline" settings; 555 | * conceal characters by making foreground color same as background. 556 | 557 | 1.22 - 5 October, 2009: 558 | - hook LoadLibrary to inject into applications started via association. 559 | 560 | 1.21 - 23 September, 2009: 561 | + -i (and -u) option to add (remove) entry to AutoRun value. 562 | 563 | 1.20 - 21 June, 2009: 564 | * use another injection method; 565 | + create ANSICON environment variable; 566 | + -e (and -E) option to echo the command line (without newline); 567 | + -t (and -T) option to type (display) files (with file name). 568 | 569 | 1.15 - 17 May, 2009: 570 | - fix output corruption for long (over 8192 characters) ANSI strings. 571 | 572 | 1.14 - 3 April, 2009: 573 | - fix the test for an empty import section (eg. XCOPY now works). 574 | 575 | 1.13 - 21 & 27 March, 2009: 576 | * use a new injection method (to work with DEP); 577 | * use Unicode. 578 | 579 | 1.12 - 9 March, 2009: 580 | - fix processing child programs (generate a relocatable DLL). 581 | 582 | 1.11 - 28 February, 2009: 583 | - fix processing child programs (only use for console executables). 584 | 585 | 1.10 - 22 February, 2009: 586 | - fix output corruption (buffer overflow in MyConsoleWriteW); 587 | - recognise current screen attributes as current ANSI atrributes; 588 | - ignore Ctrl+C and Ctrl+Break; 589 | + process child programs. 590 | 591 | 1.01 - 12 March, 2006: 592 | * \e[m will restore original color, not set grey on black; 593 | + -m option to set default (and initial) color; 594 | - restore original color on exit; 595 | - disable escape processing when console has disabled processed output; 596 | + \e[5m (blink) is the same as \e[4m (underline); 597 | - do not conceal control characters (0 to 31). 598 | 599 | 1.00 - 23 October, 2005: 600 | + initial release. 601 | 602 | 603 | Acknowledgments 604 | =============== 605 | 606 | Jean-Louis Morel, for his Perl package Win32::Console::ANSI. It provided 607 | the basis of 'ANSI.dll'. 608 | 609 | Sergey Oblomov (hoopoepg), for Console Manager. It provided the basis of 610 | 'ansicon.exe'. 611 | 612 | Anton Bassov's article "Process-wide API spying - an ultimate hack" in "The 613 | Code Project". 614 | 615 | Richard Quadling - his persistence in finding bugs has made ANSICON what it 616 | is today. 617 | 618 | Dmitry Menshikov, Marko Bozikovic and Philippe Villiers, for their assis- 619 | tance in making the 64-bit version a reality. 620 | 621 | Luis Lavena and the Ruby people for additional improvements. 622 | 623 | Leigh Hebblethwaite for documentation tweaks. 624 | 625 | Vincent Fatica for pointing out \e[K was not right. 626 | Nat Kuhn for pointing out the problem with report cursor position. 627 | Michel Kempeneers for discovering the buffer wrap issue. 628 | Jean-Luc Gautier for pointing out the problem with redirecting -e to NUL. 629 | 630 | Thiadmer Riemersma for the nearest color algorithm. 631 | 632 | 633 | Contact 634 | ======= 635 | 636 | mailto:jadoxa@yahoo.com.au 637 | http://ansicon.adoxa.vze.com/ 638 | https://github.com/adoxa/ansicon 639 | 640 | 641 | Distribution 642 | ============ 643 | 644 | The original zipfile can be freely distributed, by any means. However, I 645 | would like to be informed if it is placed on a CD-ROM (other than an arch- 646 | ive compilation; permission is granted, I'd just like to know). Modified 647 | versions may be distributed, provided it is indicated as such in the ver- 648 | sion text and a source diff is made available. In particular, the supplied 649 | binaries are freely redistributable. A formal license (zlib) is available 650 | in LICENSE.txt. 651 | 652 | 653 | =========================== 654 | Jason Hood, 29 April, 2019. 655 | --------------------------------------------------------------------------------