Many changes, bad programmer!

Just copying the history from the source:

recognize the standard handle defines in WriteFile;
minor speed improvement by caching GetConsoleMode;
keep track of three handles (ostensibly stdout, stderr and a file);
test a DOS header exists before writing to e_oemid;
more flexible/robust handling of data directories;
files writing to the console will always succeed;
log: use API file functions and a custom printf;
     add a blank line between processes;
     set function name for MyWriteConsoleA;
scan imports from "kernel32" (without extension);
added dynamic environment variable CLICOLOR;
removed _hwrite (it's the same address as _lwrite);
join multibyte characters split across separate writes;
remove wcstok, avoiding potential interference with the host;
similarly, use a private heap instead of malloc.
This commit is contained in:
Jason Hood 2017-07-25 18:18:34 +10:00
parent 2f18f10719
commit 40f59c543c
10 changed files with 943 additions and 437 deletions

649
ANSI.c

File diff suppressed because it is too large Load Diff

View File

@ -87,7 +87,7 @@
add error codes to some message. add error codes to some message.
*/ */
#define PDATE L"26 February, 2014" #define PDATE L"24 December, 2015"
#include "ansicon.h" #include "ansicon.h"
#include "version.h" #include "version.h"
@ -169,6 +169,7 @@ int my_fputws( const wchar_t* s, FILE* f )
#define _putws( s ) my_fputws( s L"\n", stdout ) #define _putws( s ) my_fputws( s L"\n", stdout )
HANDLE hHeap;
#if defined(_WIN64) #if defined(_WIN64)
LPTSTR DllNameType; LPTSTR DllNameType;
#endif #endif
@ -183,7 +184,7 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
#ifdef _WIN64 #ifdef _WIN64
if (app != NULL) if (app != NULL)
#endif #endif
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId );
type = ProcessType( ppi, &base, gui ); type = ProcessType( ppi, &base, gui );
if (type <= 0) if (type <= 0)
{ {
@ -230,7 +231,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
int type; int type;
#endif #endif
DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId ); DEBUGSTR( 1, "%S (%u)", app, ppi->dwProcessId );
// Find the base address of kernel32.dll. // Find the base address of kernel32.dll.
ticks = GetTickCount(); ticks = GetTickCount();
@ -242,7 +243,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
#ifndef _WIN64 #ifndef _WIN64
if (err == ERROR_PARTIAL_COPY) if (err == ERROR_PARTIAL_COPY)
{ {
DEBUGSTR( 1, L" Ignoring 64-bit process (use x64\\ansicon)" ); DEBUGSTR( 1, " Ignoring 64-bit process (use x64\\ansicon)" );
fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr ); fputws( L"ANSICON: parent is 64-bit (use x64\\ansicon).\n", stderr );
return; return;
} }
@ -251,10 +252,10 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
// two seconds to avoid a potentially infinite loop. // two seconds to avoid a potentially infinite loop.
if (err == ERROR_BAD_LENGTH && GetTickCount() - ticks < 2000) if (err == ERROR_BAD_LENGTH && GetTickCount() - ticks < 2000)
{ {
Sleep( 0 ); Sleep( 1 );
continue; continue;
} }
DEBUGSTR( 1, L" Unable to create snapshot (%lu)", err ); DEBUGSTR( 1, " Unable to create snapshot (%u)", err );
no_go: no_go:
fputws( L"ANSICON: unable to inject into parent.\n", stderr ); fputws( L"ANSICON: unable to inject into parent.\n", stderr );
return; return;
@ -272,7 +273,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
CloseHandle( hSnap ); CloseHandle( hSnap );
if (LLW == NULL) if (LLW == NULL)
{ {
DEBUGSTR( 1, L" Unable to locate kernel32.dll (%lu)", GetLastError() ); DEBUGSTR( 1, " Unable to locate kernel32.dll" );
goto no_go; goto no_go;
} }
@ -292,7 +293,7 @@ void RemoteLoad( LPPROCESS_INFORMATION ppi, LPCTSTR app )
mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE ); mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE );
if (mem == NULL) if (mem == NULL)
{ {
DEBUGSTR( 1, L" Unable to allocate virtual memory (%lu)", GetLastError() ); DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError());
goto no_go; goto no_go;
} }
WriteProcMem( mem, DllName, TSIZE(len + 11) ); WriteProcMem( mem, DllName, TSIZE(len + 11) );
@ -357,6 +358,8 @@ int main( void )
} }
} }
hHeap = HeapCreate( 0, 0, 65 * 1024 );
prog = get_program_name( NULL ); prog = get_program_name( NULL );
*buf = '\0'; *buf = '\0';
GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) ); GetEnvironmentVariable( L"ANSICON_LOG", buf, lenof(buf) );
@ -369,7 +372,7 @@ int main( void )
pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId ); pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId );
if (pi.hProcess == NULL) if (pi.hProcess == NULL)
{ {
DEBUGSTR( 1, L" Unable to open process %lu (%lu)", DEBUGSTR( 1, " Unable to open process %u (%u)",
pi.dwProcessId, GetLastError() ); pi.dwProcessId, GetLastError() );
} }
else else
@ -738,7 +741,7 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if (hSnap == INVALID_HANDLE_VALUE) if (hSnap == INVALID_HANDLE_VALUE)
{ {
DEBUGSTR( 1, L"Failed to create snapshot (%lu)", GetLastError() ); DEBUGSTR( 1, "Failed to create snapshot (%u)", GetLastError() );
return FALSE; return FALSE;
} }
@ -747,7 +750,7 @@ BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
CloseHandle( hSnap ); CloseHandle( hSnap );
if (!fOk) if (!fOk)
{ {
DEBUGSTR( 1, L"Failed to locate parent" ); DEBUGSTR( 1, "Failed to locate parent" );
return FALSE; return FALSE;
} }

View File

@ -35,6 +35,7 @@
// Macro for adding pointers/DWORDs together without C arithmetic interfering // Macro for adding pointers/DWORDs together without C arithmetic interfering
#define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset)) #define MakeVA( cast, offset ) (cast)((DWORD_PTR)pDosHeader + (DWORD)(offset))
#define DATADIRS OptionalHeader.NumberOfRvaAndSizes
#define EXPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] #define EXPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
#define IMPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] #define IMPORTDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
#define BOUNDDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] #define BOUNDDIR OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
@ -61,6 +62,7 @@ DWORD GetProcRVA( LPCTSTR, LPCSTR, int );
DWORD GetProcRVA( LPCTSTR, LPCSTR ); DWORD GetProcRVA( LPCTSTR, LPCSTR );
#endif #endif
extern HANDLE hHeap;
extern TCHAR prog_path[MAX_PATH]; extern TCHAR prog_path[MAX_PATH];
extern LPTSTR prog; extern LPTSTR prog;
@ -74,6 +76,6 @@ extern char* ansi_bits;
void set_ansi_dll( void ); void set_ansi_dll( void );
extern int log_level; extern int log_level;
void DEBUGSTR( int level, LPTSTR szFormat, ... ); void DEBUGSTR( int level, LPCSTR szFormat, ... );
#endif #endif

110
injdll.c
View File

@ -38,11 +38,35 @@ static PVOID FindMem( HANDLE hProcess, PBYTE base, DWORD len )
} }
// Count the imports directly (including the terminator), since the size of the
// import directory is not necessarily correct (Windows doesn't use it at all).
static DWORD sizeof_imports( LPPROCESS_INFORMATION ppi,
PBYTE pBase, DWORD rImports )
{
IMAGE_IMPORT_DESCRIPTOR import;
PIMAGE_IMPORT_DESCRIPTOR pImports;
DWORD cnt;
if (rImports == 0)
return 0;
pImports = (PIMAGE_IMPORT_DESCRIPTOR)(pBase + rImports);
cnt = 0;
do
{
++cnt;
ReadProcVar( pImports++, &import );
} while (import.Name != 0);
return cnt * sizeof(import);
}
void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase ) void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
{ {
DWORD rva; DWORD rva;
PVOID pMem; PVOID pMem;
DWORD len; DWORD len, import_size;
DWORD pr; DWORD pr;
IMAGE_DOS_HEADER DosHeader; IMAGE_DOS_HEADER DosHeader;
IMAGE_NT_HEADERS NTHeader, *pNTHeader; IMAGE_NT_HEADERS NTHeader, *pNTHeader;
@ -59,18 +83,19 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew); pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew);
ReadProcVar( pNTHeader, &NTHeader ); ReadProcVar( pNTHeader, &NTHeader );
len = 4 * PTRSZ + ansi_len + sizeof(*pImports) + NTHeader.IMPORTDIR.Size; import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
pImports = malloc( len ); len = 2 * PTRSZ + ansi_len + sizeof(*pImports) + import_size;
pImports = HeapAlloc( hHeap, 0, len );
if (pImports == NULL) if (pImports == NULL)
{ {
DEBUGSTR( 1, L" Failed to allocate memory." ); DEBUGSTR( 1, " Failed to allocate memory" );
return; return;
} }
pMem = FindMem( ppi->hProcess, pBase, len ); pMem = FindMem( ppi->hProcess, pBase, len );
if (pMem == NULL) if (pMem == NULL)
{ {
DEBUGSTR( 1, L" Failed to allocate virtual memory." ); DEBUGSTR( 1, " Failed to allocate virtual memory (%u)", GetLastError() );
free( pImports ); HeapFree( hHeap, 0, pImports );
return; return;
} }
rva = (DWORD)((PBYTE)pMem - pBase); rva = (DWORD)((PBYTE)pMem - pBase);
@ -78,37 +103,39 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
ip.pL = (PLONG_PTR)pImports; ip.pL = (PLONG_PTR)pImports;
*ip.pL++ = IMAGE_ORDINAL_FLAG + 1; *ip.pL++ = IMAGE_ORDINAL_FLAG + 1;
*ip.pL++ = 0; *ip.pL++ = 0;
*ip.pL++ = IMAGE_ORDINAL_FLAG + 1;
*ip.pL++ = 0;
memcpy( ip.pB, ansi_dll, ansi_len ); memcpy( ip.pB, ansi_dll, ansi_len );
ip.pB += ansi_len; ip.pB += ansi_len;
ip.pI->OriginalFirstThunk = rva + 2 * PTRSZ; ip.pI->OriginalFirstThunk = 0;
ip.pI->TimeDateStamp = 0; ip.pI->TimeDateStamp = 0;
ip.pI->ForwarderChain = 0; ip.pI->ForwarderChain = 0;
ip.pI->Name = rva + 4 * PTRSZ; ip.pI->Name = rva + 2 * PTRSZ;
ip.pI->FirstThunk = rva; ip.pI->FirstThunk = rva;
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress, ReadProcMem( pBase+NTHeader.IMPORTDIR.VirtualAddress, ip.pI+1, import_size );
ip.pI + 1, NTHeader.IMPORTDIR.Size );
WriteProcMem( pMem, pImports, len ); WriteProcMem( pMem, pImports, len );
free( pImports ); HeapFree( hHeap, 0, pImports );
// If there's no IAT, copy the original IDT (to allow writable ".idata"). // If there's no IAT, copy the original IDT (to allow writable ".idata").
if (NTHeader.IATDIR.VirtualAddress == 0) if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_IAT &&
NTHeader.IATDIR.VirtualAddress == 0)
NTHeader.IATDIR = NTHeader.IMPORTDIR; NTHeader.IATDIR = NTHeader.IMPORTDIR;
NTHeader.IMPORTDIR.VirtualAddress = rva + 4 * PTRSZ + ansi_len; NTHeader.IMPORTDIR.VirtualAddress = rva + 2 * PTRSZ + ansi_len;
NTHeader.IMPORTDIR.Size += sizeof(*pImports); //NTHeader.IMPORTDIR.Size += sizeof(*pImports);
// Remove bound imports, so the updated import table is used. // Remove bound imports, so the updated import table is used.
NTHeader.BOUNDDIR.VirtualAddress = 0; if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT)
NTHeader.BOUNDDIR.Size = 0; {
NTHeader.BOUNDDIR.VirtualAddress = 0;
//NTHeader.BOUNDDIR.Size = 0;
}
VirtProtVar( pNTHeader, PAGE_READWRITE ); VirtProtVar( pNTHeader, PAGE_READWRITE );
WriteProcVar( pNTHeader, &NTHeader ); WriteProcVar( pNTHeader, &NTHeader );
VirtProtVar( pNTHeader, pr ); VirtProtVar( pNTHeader, pr );
// Remove the IL-only flag on a managed process. // Remove the IL-only flag on a managed process.
if (NTHeader.COMDIR.VirtualAddress != 0 && NTHeader.COMDIR.Size != 0) if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR &&
NTHeader.COMDIR.VirtualAddress != 0)
{ {
pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress); pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress);
ReadProcVar( pComHeader, &ComHeader ); ReadProcVar( pComHeader, &ComHeader );
@ -128,7 +155,7 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
{ {
DWORD rva; DWORD rva;
PVOID pMem; PVOID pMem;
DWORD len; DWORD len, import_size;
DWORD pr; DWORD pr;
IMAGE_DOS_HEADER DosHeader; IMAGE_DOS_HEADER DosHeader;
IMAGE_NT_HEADERS32 NTHeader, *pNTHeader; IMAGE_NT_HEADERS32 NTHeader, *pNTHeader;
@ -145,18 +172,19 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew); pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew);
ReadProcVar( pNTHeader, &NTHeader ); ReadProcVar( pNTHeader, &NTHeader );
len = 16 + ansi_len + sizeof(*pImports) + NTHeader.IMPORTDIR.Size; import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
pImports = malloc( len ); len = 8 + ansi_len + sizeof(*pImports) + import_size;
pImports = HeapAlloc( hHeap, 0, len );
if (pImports == NULL) if (pImports == NULL)
{ {
DEBUGSTR( 1, L" Failed to allocate memory." ); DEBUGSTR( 1, " Failed to allocate memory" );
return; return;
} }
pMem = FindMem( ppi->hProcess, pBase, len ); pMem = FindMem( ppi->hProcess, pBase, len );
if (pMem == NULL) if (pMem == NULL)
{ {
DEBUGSTR( 1, L" Failed to allocate virtual memory." ); DEBUGSTR( 1, " Failed to allocate virtual memory" );
free( pImports ); HeapFree( hHeap, 0, pImports );
return; return;
} }
rva = (DWORD)((PBYTE)pMem - pBase); rva = (DWORD)((PBYTE)pMem - pBase);
@ -164,31 +192,33 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
ip.pL = (PLONG)pImports; ip.pL = (PLONG)pImports;
*ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1; *ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1;
*ip.pL++ = 0; *ip.pL++ = 0;
*ip.pL++ = IMAGE_ORDINAL_FLAG32 + 1;
*ip.pL++ = 0;
memcpy( ip.pB, ansi_dll, ansi_len ); memcpy( ip.pB, ansi_dll, ansi_len );
ip.pB += ansi_len; ip.pB += ansi_len;
ip.pI->OriginalFirstThunk = rva + 8; ip.pI->OriginalFirstThunk = 0;
ip.pI->TimeDateStamp = 0; ip.pI->TimeDateStamp = 0;
ip.pI->ForwarderChain = 0; ip.pI->ForwarderChain = 0;
ip.pI->Name = rva + 16; ip.pI->Name = rva + 8;
ip.pI->FirstThunk = rva; ip.pI->FirstThunk = rva;
ReadProcMem( pBase + NTHeader.IMPORTDIR.VirtualAddress, ReadProcMem( pBase+NTHeader.IMPORTDIR.VirtualAddress, ip.pI+1, import_size );
ip.pI + 1, NTHeader.IMPORTDIR.Size );
WriteProcMem( pMem, pImports, len ); WriteProcMem( pMem, pImports, len );
free( pImports ); HeapFree( hHeap, 0, pImports );
if (NTHeader.IATDIR.VirtualAddress == 0) if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_IAT &&
NTHeader.IATDIR.VirtualAddress == 0)
NTHeader.IATDIR = NTHeader.IMPORTDIR; NTHeader.IATDIR = NTHeader.IMPORTDIR;
NTHeader.IMPORTDIR.VirtualAddress = rva + 16 + ansi_len; NTHeader.IMPORTDIR.VirtualAddress = rva + 8 + ansi_len;
NTHeader.IMPORTDIR.Size += sizeof(*pImports); //NTHeader.IMPORTDIR.Size += sizeof(*pImports);
NTHeader.BOUNDDIR.VirtualAddress = 0; if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT)
NTHeader.BOUNDDIR.Size = 0; {
NTHeader.BOUNDDIR.VirtualAddress = 0;
//NTHeader.BOUNDDIR.Size = 0;
}
VirtProtVar( pNTHeader, PAGE_READWRITE ); VirtProtVar( pNTHeader, PAGE_READWRITE );
WriteProcVar( pNTHeader, &NTHeader ); WriteProcVar( pNTHeader, &NTHeader );
VirtProtVar( pNTHeader, pr ); VirtProtVar( pNTHeader, pr );
if (NTHeader.COMDIR.VirtualAddress != 0 && NTHeader.COMDIR.Size != 0) if (NTHeader.DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR &&
NTHeader.COMDIR.VirtualAddress != 0)
{ {
pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress); pComHeader = (PIMAGE_COR20_HEADER)(pBase + NTHeader.COMDIR.VirtualAddress);
ReadProcVar( pComHeader, &ComHeader ); ReadProcVar( pComHeader, &ComHeader );
@ -233,7 +263,7 @@ static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi )
} }
} }
DEBUGSTR( 1, L" Failed to find ntdll.dll!" ); DEBUGSTR( 1, " Failed to find ntdll.dll!" );
return NULL; return NULL;
} }
@ -266,7 +296,7 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi )
PAGE_EXECUTE_READ ); PAGE_EXECUTE_READ );
if (pMem == NULL) if (pMem == NULL)
{ {
DEBUGSTR( 1, L" Failed to allocate virtual memory (%lu)", GetLastError() ); DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError());
return; return;
} }

View File

@ -43,10 +43,10 @@ DWORD GetProcRVA( LPCTSTR module, LPCSTR func )
if (hMod == NULL) if (hMod == NULL)
{ {
#ifdef _WIN64 #ifdef _WIN64
DEBUGSTR( 1, L"Unable to load %d-bit %s (%lu)!", DEBUGSTR( 1, "Unable to load %u-bit %S (%u)!",
bits, module, GetLastError() ); bits, module, GetLastError() );
#else #else
DEBUGSTR( 1, L"Unable to load %s (%lu)!", module, GetLastError() ); DEBUGSTR( 1, "Unable to load %S (%u)!", module, GetLastError() );
#endif #endif
return 0; return 0;
} }
@ -70,9 +70,9 @@ DWORD GetProcRVA( LPCTSTR module, LPCSTR func )
if (pFunc == NULL) if (pFunc == NULL)
{ {
#ifdef _WIN64 #ifdef _WIN64
DEBUGSTR( 1, L"Could not find %d-bit %s!", bits, func ); DEBUGSTR( 1, "Could not find %u-bit %s!", bits, func );
#else #else
DEBUGSTR( 1, L"Could not find %s!", func ); DEBUGSTR( 1, "Could not find %s!", func );
#endif #endif
rva = 0; rva = 0;
} }

View File

@ -76,8 +76,8 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
{ {
PIMAGE_NT_HEADERS32 pNTHeader = (PIMAGE_NT_HEADERS32)&nt_header; PIMAGE_NT_HEADERS32 pNTHeader = (PIMAGE_NT_HEADERS32)&nt_header;
if (pNTHeader->COMDIR.VirtualAddress != 0 && if (pNTHeader->DATADIRS > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR &&
pNTHeader->COMDIR.Size != 0) pNTHeader->COMDIR.VirtualAddress != 0)
{ {
IMAGE_COR20_HEADER ComHeader, *pComHeader; IMAGE_COR20_HEADER ComHeader, *pComHeader;
pComHeader = (PIMAGE_COR20_HEADER)((PBYTE)minfo.BaseAddress pComHeader = (PIMAGE_COR20_HEADER)((PBYTE)minfo.BaseAddress
@ -87,57 +87,51 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
!(ComHeader.Flags & COMIMAGE_FLAGS_32BITREQUIRED)) !(ComHeader.Flags & COMIMAGE_FLAGS_32BITREQUIRED))
{ {
#if defined(_WIN64) || !defined(W32ON64) // W32ON64 will log due to -P #if defined(_WIN64) || !defined(W32ON64) // W32ON64 will log due to -P
DEBUGSTR( 1, L" AnyCPU %s (base = %.8X)", DEBUGSTR( 1, " AnyCPU %s (base = %q)",
(*gui) ? L"GUI" : L"console", (*gui) ? "GUI" : "console", minfo.BaseAddress );
PtrToUint( minfo.BaseAddress ) );
#endif #endif
#if defined(_WIN64) || defined(W32ON64) #if defined(_WIN64) || defined(W32ON64)
return 48; return 48;
#else #else
if (ProcessIs64( ppi->hProcess )) if (ProcessIs64( ppi->hProcess ))
{ {
DEBUGSTR( 1, L" Unsupported (use x64\\ansicon)" ); DEBUGSTR( 1, " Unsupported (use x64\\ansicon)" );
return 0; return 0;
} }
return 32; return 32;
#endif #endif
} }
} }
DEBUGSTR( 1, L" 32-bit %s (base = %.8X)", DEBUGSTR( 1, " 32-bit %s (base = %q)",
(*gui) ? L"GUI" : L"console", (*gui) ? "GUI" : "console", minfo.BaseAddress );
PtrToUint( minfo.BaseAddress ) );
return 32; return 32;
} }
if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
{ {
#ifdef _WIN64 #ifdef _WIN64
DEBUGSTR( 1, L" 64-bit %s (base = %.8X_%.8X)", DEBUGSTR( 1, " 64-bit %s (base = %p)",
(*gui) ? L"GUI" : L"console", (*gui) ? "GUI" : "console", minfo.BaseAddress );
(DWORD)((DWORD_PTR)minfo.BaseAddress >> 32),
PtrToUint( minfo.BaseAddress ) );
return 64; return 64;
#elif defined(W32ON64) #elif defined(W32ON64)
// Console will log due to -P, but GUI may be ignored (if not, // Console will log due to -P, but GUI may be ignored (if not,
// this'll show up twice). // this'll show up twice).
if (*gui) if (*gui)
DEBUGSTR( 1, L" 64-bit GUI (base = 00000000_%.8X)", DEBUGSTR( 1, " 64-bit GUI (base = %P)", minfo.BaseAddress );
PtrToUint( minfo.BaseAddress ) );
return 64; return 64;
#else #else
DEBUGSTR( 1, L" 64-bit %s (base = 00000000_%.8X)", DEBUGSTR( 1, " 64-bit %s (base = %P)",
(*gui) ? L"GUI" : L"console", (*gui) ? "GUI" : "console", minfo.BaseAddress );
PtrToUint( minfo.BaseAddress ) ); DEBUGSTR( 1, " Unsupported (use x64\\ansicon)" );
DEBUGSTR( 1, L" Unsupported (use x64\\ansicon)" );
return 0; return 0;
#endif #endif
} }
DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)", DEBUGSTR( 1, " Ignoring unsupported machine (0x%X)",
nt_header.FileHeader.Machine ); nt_header.FileHeader.Machine );
} }
else else
{ {
DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)", DEBUGSTR( 1, " Ignoring unsupported subsystem (%u)",
nt_header.OptionalHeader.Subsystem ); nt_header.OptionalHeader.Subsystem );
} }
return 0; return 0;
} }
@ -147,16 +141,16 @@ int ProcessType( LPPROCESS_INFORMATION ppi, PBYTE* pBase, BOOL* gui )
if (((DWORD)ptr >> 12) + ((DWORD)minfo.RegionSize >> 12) >= 0x100000) if (((DWORD)ptr >> 12) + ((DWORD)minfo.RegionSize >> 12) >= 0x100000)
{ {
#ifdef W32ON64 #ifdef W32ON64
DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit" ); DEBUGSTR( 1, " Pointer overflow: assuming 64-bit" );
return 64; return 64;
#else #else
DEBUGSTR( 1, L" Ignoring apparent 64-bit process (use x64\\ansicon)" ); DEBUGSTR( 1, " Ignoring apparent 64-bit process (use x64\\ansicon)" );
return 0; return 0;
#endif #endif
} }
#endif #endif
} }
DEBUGSTR( 1, L" Ignoring non-Windows process" ); DEBUGSTR( 1, " Ignoring non-Windows process" );
return 0; return 0;
} }

110
readme.md
View File

@ -5,8 +5,8 @@ provides much the same functionality as `ANSI.SYS` does for MS-DOS.
## Requirements ## Requirements
* 32-bit: Windows 2000 Professional or later (it won't work with NT or 9X). * 32-bit: Windows 2000 Professional and later (it won't work with NT or 9X).
* 64-bit: Vista or later (it won't work with XP64). * 64-bit: AMD64 (it won't work with IA64).
## Installation ## Installation
@ -111,15 +111,17 @@ Using `ansicon` after install will always start with the default attributes,
restoring the originals on exit; all other programs will use the current restoring the originals on exit; all other programs will use the current
attributes. The shift state is always reset for a new process. attributes. The shift state is always reset for a new process.
The Windows API `WriteFile` and `WriteConsoleA` functions will set the number of My version of `WriteConsoleA` will always set the number of characters written,
characters written, not the number of bytes. When using a multibyte character not the number of bytes. This means writing a double-byte character as two
set, this results in a smaller number (since multiple bytes are used to bytes will set 0 the first write (nothing was written) and 1 the second (when
represent a single character). Some programs recognise this as a reduced write the character was actually written); Windows normally sets 1 for both writes.
and will inadvertently repeat previous characters. If you discover such a Similarly, writing the individual bytes of a multibyte character will set 0 for
program, use the `ANSICON_API` environment variable to record it and override all but the last byte, then 1 on the last; Windows normally sets 1 for each
the API, returning the original byte count. Ruby (prior to 1.9.3) is an example byte, writing the undefined character. However, my `WriteFile` (and
of such a program, so use `set ANSICON_API=ruby` to avoid the repitition. The `_lwrite`/`_hwrite`) will always set what was received; Windows, using a
full syntax is: multibyte character set (but not DBCS), would set the characters. You can have
`WriteConsoleA` return the original byte count by using the `ANSICON_API`
environment variable:
ANSICON_API=[!]program;program;program... ANSICON_API=[!]program;program;program...
@ -127,14 +129,16 @@ PROGRAM is the name of the program, with no path and extension. The leading
exclamation inverts the usage, meaning the API will always be overridden, unless exclamation inverts the usage, meaning the API will always be overridden, unless
the program is in the list. The variable can be made permanent by going to the program is in the list. The variable can be made permanent by going to
_System Properties_, selecting the _Advanced_ tab (with Vista onwards, this can _System Properties_, selecting the _Advanced_ tab (with Vista onwards, this can
be done by running _"SystemPropertiesAdvanced"_) and clicking _Environment be done by running `SystemPropertiesAdvanced`) and clicking _Environment
Variables_. Variables_.
## Limitations ## Limitations
- The entire console buffer is used, not just the visible window. - Line sequences use the window; column sequences use the buffer.
- There's a conflict with NVIDIA's drivers, requiring the setting of the - An application using multiple screen buffers will not have separate
attributes in each buffer.
- There may be a conflict with NVIDIA's drivers, requiring the setting of the
Environment Variable: Environment Variable:
ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll
@ -146,38 +150,46 @@ Variables_.
The following escape sequences are recognised. The following escape sequences are recognised.
\e]0;titleBEL Set (xterm) window's title (and icon) \e]0;titleBEL xterm: Set window's title (and icon, ignored)
\e[21t Report (xterm) window's title \e]2;titleBEL xterm: Set window's title
\e[s Save Cursor \e[21t xterm: Report window's title
\e[u Restore Cursor \e[s ANSI.SYS: Save Cursor Position
\e[#G CHA Cursor Character Absolute \e[u ANSI.SYS: Restore Cursor Position
\e[#E CNL Cursor Next Line \e[#Z CBT Cursor Backward Tabulation
\e[#F CPL Cursor Preceding Line \e[#G CHA Cursor Character Absolute
\e[#D CUB Cursor Left \e[#I CHT Cursor Forward Tabulation
\e[#B CUD Cursor Down \e[#E CNL Cursor Next Line
\e[#C CUF Cursor Right \e[#F CPL Cursor Preceding Line
\e[#;#H CUP Cursor Position \e[3h CRM Control Representation Mode (display controls)
\e[#A CUU Cursor Up \e[3l CRM Control Representation Mode (perform controls)
\e[#P DCH Delete Character \e[#D CUB Cursor Left
\e[#B CUD Cursor Down
\e[#C CUF Cursor Right
\e[#;#H CUP Cursor Position
\e[#A CUU Cursor Up
\e[#P DCH Delete Character
\e[?7h DECAWM DEC Autowrap Mode (autowrap)
\e[?7l DECAWM DEC Autowrap Mode (no autowrap)
\e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor) \e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor)
\e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor) \e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor)
\e[#M DL Delete Line \e[#M DL Delete Line
\e[#n DSR Device Status Report \e[#n DSR Device Status Report
\e[#X ECH Erase Character \e[#X ECH Erase Character
\e[#J ED Erase In Page \e[#J ED Erase In Page
\e[#K EL Erase In Line \e[#K EL Erase In Line
\e[#` HPA Character Position Absolute \e[#` HPA Character Position Absolute
\e[#j HPB Character Position Backward \e[#j HPB Character Position Backward
\e[#a HPR Character Position Forward \e[#a HPR Character Position Forward
\e[#;#f HVP Character And Line Position \e[#;#f HVP Character And Line Position
\e[#@ ICH Insert Character \e[#@ ICH Insert Character
\e[#L IL Insert Line \e[#L IL Insert Line
SI LS0 Locking-shift Zero (see below) SI LS0 Locking-shift Zero (see below)
SO LS1 Locking-shift One SO LS1 Locking-shift One
\e[#;#;#m SGR Select Graphic Rendition \e[#b REP Repeat
\e[#d VPA Line Position Absolute \e[#;#;#m SGR Select Graphic Rendition
\e[#k VPB Line Position Backward \e[#d VPA Line Position Absolute
\e[#e VPR Line Position Forward \e[#k VPB Line Position Backward
\e[#e VPR Line Position Forward
- `\e` represents the escape character (ASCII 27). - `\e` represents the escape character (ASCII 27).
- `#` represents a decimal number (optional, in most cases defaulting to 1). - `#` represents a decimal number (optional, in most cases defaulting to 1).
@ -193,6 +205,12 @@ the latter will explicitly reset them. The environment variable `ANSICON_DEF`
can be used to change the default colors (same value as `-m`; setting the can be used to change the default colors (same value as `-m`; setting the
variable does not change the current colors). variable does not change the current colors).
The first time a program clears the screen (`\e[2J`) will actually scroll in a
new window (assuming the buffer is bigger than the window, of course).
Subsequent clears will then blank the window. However, if the window has
scrolled, or the cursor is on the last line of the buffer, it will again scroll
in a new window.
### Ignored Sequences ### Ignored Sequences
@ -214,7 +232,7 @@ http://vt100.net/docs/vt220-rm/table2-4.html.
Char Unicode Code Point & Name Char Unicode Code Point & Name
---- ------------------------- ---- -------------------------
_ U+0020 Space (blank) _ U+00A0 No-Break Space (blank)
` U+2666 Black Diamond Suit ` U+2666 Black Diamond Suit
a U+2592 Medium Shade a U+2592 Medium Shade
b U+2409 Symbol For Horizontal Tabulation b U+2409 Symbol For Horizontal Tabulation
@ -299,4 +317,4 @@ In particular, the supplied binaries are freely redistributable.
A formal license (zlib) is available in `LICENSE.txt`. A formal license (zlib) is available in `LICENSE.txt`.
--- ---
Copyright 2005-2014 Jason Hood Copyright 2005-2015 Jason Hood

View File

@ -1,9 +1,9 @@
ANSICON ANSICON
Copyright 2005-2014 Jason Hood Copyright 2005-2015 Jason Hood
Version 1.71. Freeware Version 1.72. Freeware
Description Description
@ -110,9 +110,10 @@ Usage
The variable is updated whenever a program reads it directly (i.e. as an The variable is updated whenever a program reads it directly (i.e. as an
individual request, not as part of the entire environment block). For individual request, not as part of the entire environment block). For
example, 'set an' will not update it, but 'echo %ansicon%' will. Also example, 'set an' will not update it, but 'echo %ansicon%' will. Also
created is ANSICON_VER, which contains the version without the point (1.50 created are ANSICON_VER, which contains the version without the point (1.50
becomes "150"). This variable does not exist as part of the environment becomes "150"), and CLICOLOR (see http://bixense.com/clicolors/), which
block ('set an' will not show it). contains "1". These variables do not exist as part of the environment
block (e.g. 'set an' will not show ANSICON_VER).
If installed, GUI programs will not be hooked. Either start the program If installed, GUI programs will not be hooked. Either start the program
directly with 'ansicon', or add it to the ANSICON_GUI variable (see directly with 'ansicon', or add it to the ANSICON_GUI variable (see
@ -122,15 +123,17 @@ Usage
utes, restoring the originals on exit; all other programs will use the cur- utes, restoring the originals on exit; all other programs will use the cur-
rent attributes. The shift state is always reset for a new process. rent attributes. The shift state is always reset for a new process.
The Windows API WriteFile and WriteConsoleA functions will set the number My version of WriteConsoleA will always set the number of characters writt-
of characters written, not the number of bytes. When using a multibyte en, not the number of bytes. This means writing a double-byte character as
character set, this results in a smaller number (since multiple bytes are two bytes will set 0 the first write (nothing was written) and 1 the second
used to represent a single character). Some programs recognise this as a (when the character was actually written); Windows normally sets 1 for both
reduced write and will inadvertently repeat previous characters. If you writes. Similarly, writing the individual bytes of a multibyte character
discover such a program, use the ANSICON_API environment variable to record will set 0 for all but the last byte, then 1 on the last; Windows normally
it and override the API, returning the original byte count. Ruby (prior to sets 1 for each byte, writing the undefined character. However, my
1.9.3) is an example of such a program, so use 'set ANSICON_API=ruby' to WriteFile (and _lwrite/_hwrite) will always set what was received; Windows,
avoid the repitition. The full syntax is: using a multibyte character set (but not DBCS), would set the characters.
You can have WriteConsoleA return the original byte count by using the
ANSICON_API environment variable:
ANSICON_API=[!]program;program;program... ANSICON_API=[!]program;program;program...
@ -277,7 +280,7 @@ Limitations
Line sequences use the window; column sequences use the buffer. Line sequences use the window; column sequences use the buffer.
Tabs are fixed at eight columns. Tabs are fixed at eight columns.
There's a conflict with NVIDIA's drivers, requiring the setting of the There may be a conflict with NVIDIA's drivers, requiring the setting of the
Environment Variable: Environment Variable:
ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll
@ -291,8 +294,24 @@ Version History
Legend: + added, - bug-fixed, * changed. Legend: + added, - bug-fixed, * changed.
1.72 - 24 December, 2015:
- handle STD_OUTPUT_HANDLE & STD_ERROR_HANDLE in WriteFile;
- better handling of unusual PE files;
* cache GetConsoleMode for an improvement in speed;
* files writing to the console always succeed (should mostly remove the
need for ANSICON_API);
* log: add a blank line between processes;
remove the separate line for WriteFile & _lwrite;
write byte strings as-is, wide strings using the current code page;
use caret notation for control characters, with hexadecimal "^xNN"
and "^uNNNN" for characters not in the code page (custom printf);
* join multibyte characters split across separate writes;
* remove wcstok, avoiding potential interference with the host program;
* similarly, remove malloc & friends, using a private heap;
+ add CLICOLOR dynamic environment variable.
1.71 - 23 October, 2015: 1.71 - 23 October, 2015:
+ add _CRT_NON_CONFORMING_WCSTOK define + add _CRT_NON_CONFORMING_WCSTOK define for VS2015.
1.70 - 26 February, 2014: 1.70 - 26 February, 2014:
- don't hook again if using LoadLibrary or LoadLibraryEx; - don't hook again if using LoadLibrary or LoadLibraryEx;
@ -501,4 +520,4 @@ Distribution
============================== ==============================
Jason Hood, 26 February, 2014. Jason Hood, 24 December, 2015.

365
util.c
View File

@ -74,99 +74,316 @@ void set_ansi_dll( void )
} }
void DEBUGSTR( int level, LPTSTR szFormat, ... ) static LPSTR buf;
static DWORD buf_len;
static BOOL quote, alt;
static DWORD str_format( DWORD pos, BOOL wide, DWORD_PTR str, DWORD len )
{ {
static char tempfile[MAX_PATH]; static UINT cp;
static DWORD pid; static DWORD flags;
static BOOL def, *pDef, start_trail;
TCHAR szBuffer[1024], szEscape[1024]; union
va_list pArgList;
HANDLE mutex;
DWORD wait;
FILE* file;
if ((log_level & 3) < level && !(level & 4 & log_level))
return;
if (*tempfile == '\0')
{ {
_snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) ); LPSTR a;
pid = GetCurrentProcessId(); LPWSTR w;
} src;
int ch;
BOOL trail;
src.a = (LPSTR)str;
if (len == 0 && str != 0)
len = (DWORD)(wide ? wcslen( src.w ) : strlen( src.a ));
if (pos + len * 6 + 8 >= buf_len)
{
LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + len * 6 + 8 );
if (tmp == NULL)
return 0;
buf = tmp;
buf_len = (DWORD)HeapSize( hHeap, 0, buf );
} }
if (szFormat == NULL)
if (len == 0)
{ {
// Explicitly use 't', as _fmode might be binary. if (str == 0)
file = fopen( tempfile, (log_level & 8) ? "at" : "wt" ); pos += wsprintfA( buf + pos, "<null>" );
if (file != NULL) else if (quote)
{ {
SYSTEMTIME now; buf[pos++] = '"';
GetLocalTime( &now ); buf[pos++] = '"';
fseek( file, 0, SEEK_END );
if (ftell( file ) != 0)
putc( '\n', file );
fprintf( file, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d) started "
"%d-%.2d-%.2d %d:%.2d:%.2d\n",
log_level,
now.wYear, now.wMonth, now.wDay,
now.wHour, now.wMinute, now.wSecond );
fclose( file );
} }
return; else if (alt)
pos += wsprintfA( buf + pos, "<empty>" );
return pos;
} }
va_start( pArgList, szFormat ); if (cp != GetConsoleOutputCP())
_vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList );
va_end( pArgList );
szFormat = szBuffer;
if (*szFormat == '\33')
{ {
BOOL first = TRUE; cp = GetConsoleOutputCP();
LPTSTR pos = szEscape; if (wide)
while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4)
{ {
if (*szFormat < 32) wchar_t und = L'\xFFFF';
flags = WC_NO_BEST_FIT_CHARS;
pDef = &def;
// Some code pages don't support the default character.
if (!WideCharToMultiByte( cp, flags, &und, 1, buf + pos, 12, NULL, pDef ))
{ {
*pos++ = '\\'; flags = 0;
switch (*szFormat) pDef = NULL;
def = FALSE;
}
}
}
if (quote)
buf[pos++] = '"';
trail = FALSE;
while (len-- != 0)
{
if (wide)
ch = *src.w++;
else
ch = (BYTE)*src.a++;
if (ch < 32 || (quote && start_trail))
{
start_trail = FALSE;
if (quote)
{
buf[pos++] = '\\';
switch (ch)
{ {
case '\a': *pos++ = 'a'; break; case '\0': buf[pos++] = '0'; break;
case '\b': *pos++ = 'b'; break; case '\a': buf[pos++] = 'a'; break;
case '\t': *pos++ = 't'; break; case '\b': buf[pos++] = 'b'; break;
case '\r': *pos++ = 'r'; break; case '\t': buf[pos++] = 't'; break;
case '\n': *pos++ = 'n'; break; case '\n': buf[pos++] = 'n'; break;
case 27 : *pos++ = 'e'; break; case '\v': buf[pos++] = 'v'; break;
case '\f': buf[pos++] = 'f'; break;
case '\r': buf[pos++] = 'r'; break;
case 27 : buf[pos++] = 'e'; break;
default: default:
pos += _snwprintf( pos, 32, L"%.*o", pos += wsprintfA( buf + pos, "x%.2X", ch );
(szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1,
*szFormat );
} }
} }
else else
{ {
if (*szFormat == '"') buf[pos++] = '^';
{ buf[pos++] = ch + '@';
if (first)
first = FALSE;
else if (szFormat[1] != '\0')
*pos++ = '\\';
}
*pos++ = *szFormat;
} }
} }
*pos = '\0'; else if (quote && ch == '"')
szFormat = szEscape; {
buf[pos++] = '\\';
buf[pos++] = ch;
}
else if (!wide)
{
if (quote && (cp == 932 || cp == 936 || cp == 949 || cp == 950))
{
if (trail)
trail = FALSE;
else if (IsDBCSLeadByteEx( cp, (char)ch ))
{
if (len == 0)
start_trail = TRUE;
else
trail = TRUE;
}
}
if (quote && start_trail)
pos += wsprintfA( buf + pos, "\\x%.2X", ch );
else
buf[pos++] = ch;
}
else
{
int mb = WideCharToMultiByte( cp, flags, src.w - 1, 1, buf + pos, 12,
NULL, pDef );
if (def)
mb = wsprintfA( buf + pos, ch < 0x100 ? "%cx%.2X" : "%cu%.4X",
(quote) ? '\\' : '^', ch );
pos += mb;
}
} }
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" ); if (quote)
wait = WaitForSingleObject( mutex, 500 ); buf[pos++] = '"';
file = fopen( tempfile, "at" );
if (file != NULL) return pos;
{ }
fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat );
fclose( file ); void DEBUGSTR( int level, LPCSTR szFormat, ... )
} {
if (wait == WAIT_OBJECT_0) static int prefix_len;
ReleaseMutex( mutex ); static HANDLE mutex;
CloseHandle( mutex ); static DWORD size;
WCHAR temp[MAX_PATH];
HANDLE file;
va_list pArgList;
DWORD len, slen, written;
DWORD_PTR num;
if ((log_level & 3) < level && !(level & 4 & log_level))
return;
if (mutex == NULL)
{
mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
if (mutex == NULL)
{
file = INVALID_HANDLE_VALUE;
return;
}
buf = HeapAlloc( hHeap, 0, 2048 );
buf_len = (DWORD)HeapSize( hHeap, 0, buf );
prefix_len = wsprintfA( buf, "%S (%lu): ", prog, GetCurrentProcessId() );
}
if (WaitForSingleObject( mutex, 500 ) == WAIT_TIMEOUT)
return;
ExpandEnvironmentStrings( L"%TEMP%\\ansicon.log", temp, lenof(temp) );
file = CreateFile( temp, GENERIC_WRITE, FILE_SHARE_READ, NULL,
(szFormat != NULL || (log_level & 8)) ? OPEN_ALWAYS
: CREATE_ALWAYS,
0, NULL );
if (file == INVALID_HANDLE_VALUE)
{
ReleaseMutex( mutex );
CloseHandle( mutex );
return;
}
len = SetFilePointer( file, 0, NULL, FILE_END );
if (len == 0 || szFormat == NULL)
{
char buf[128];
SYSTEMTIME now;
size = 0;
if (len != 0)
{
memset( buf + 2, '=', 72 );
buf[0] = buf[74] = buf[76] = '\r';
buf[1] = buf[75] = buf[77] = '\n';
WriteFile( file, buf, 78, &written, NULL );
}
GetLocalTime( &now );
len = wsprintfA( buf, "ANSICON (" BITSA "-bit) v" PVERSA " log (%d)"
" started %d-%.2d-%.2d %d:%.2d:%.2d\r\n",
log_level,
now.wYear, now.wMonth, now.wDay,
now.wHour, now.wMinute, now.wSecond );
WriteFile( file, buf, len, &written, NULL );
if (szFormat == NULL)
{
CloseHandle( file );
ReleaseMutex( mutex );
return;
}
}
if (len != size)
WriteFile( file, "\r\n", 2, &written, NULL );
va_start( pArgList, szFormat );
// Customized printf, mainly to handle wide-character strings the way I want.
// It only supports:
// %u unsigned 32-bit decimal
// %X unsigned 32-bit upper case hexadecimal
// %p native pointer
// %q native pointer, display as 32 bits
// %P 32-bit pointer, display as 64 bits
// %s null-terminated byte characters
// %S null-terminated wide characters
//
// s & S may be prefixed with (in this order):
// " quote the string, using C-style escapes and <null> for NULL
// # use <null> for NULL and <empty> for ""
// < length of the string is the previous %u
// * length of the string is the parameter before the string
//
// Wide strings are converted according to the current code page; if a
// character could not be translated, hex is used.
//
// C-style escapes are the standard backslash sequences, plus '\e' for ESC,
// with '\x' used for two hex digits and '\u' for four. Otherwise, caret
// notation is used to represent controls, with '^x'/'^u' for hex.
num = 0;
len = prefix_len;
while (*szFormat != '\0')
{
if (*szFormat != '%')
buf[len++] = *szFormat++;
else
{
quote = alt = FALSE;
++szFormat;
if (*szFormat == '"')
{
quote = TRUE;
++szFormat;
}
if (*szFormat == '#')
{
alt = TRUE;
++szFormat;
}
slen = 0;
if (*szFormat == '<')
{
slen = (DWORD)num;
++szFormat;
}
if (*szFormat == '*')
{
slen = va_arg( pArgList, DWORD );
++szFormat;
}
num = va_arg( pArgList, DWORD_PTR );
switch (*szFormat++)
{
case 'u': len += wsprintfA( buf + len, "%u", (DWORD)num ); break;
case 'X': len += wsprintfA( buf + len, "%X", (DWORD)num ); break;
case 'p':
#ifdef _WIN64
len += wsprintfA( buf + len, "%.8X_%.8X",
(DWORD)(num >> 32), (DWORD)num );
break;
#endif
case 'q': len += wsprintfA( buf + len, "%.8X", (DWORD)num ); break;
case 'P': len += wsprintfA( buf + len, "00000000_%.8X", (DWORD)num ); break;
case 's': len = str_format( len, FALSE, num, slen ); break;
case 'S': len = str_format( len, TRUE, num, slen ); break;
default:
buf[len++] = '%';
if (szFormat[-1] == '\0')
--szFormat;
else
buf[len++] = szFormat[-1];
}
if (len >= buf_len - 20)
{
LPVOID tmp = HeapReAlloc( hHeap, 0, buf, buf_len + 128 );
if (tmp == NULL)
break;
buf = tmp;
buf_len = (DWORD)HeapSize( hHeap, 0, buf );
}
}
}
buf[len++] = '\r';
buf[len++] = '\n';
WriteFile( file, buf, len, &written, NULL );
size = GetFileSize( file, NULL );
CloseHandle( file );
ReleaseMutex( mutex );
} }

View File

@ -2,11 +2,11 @@
version.h - Version defines. version.h - Version defines.
*/ */
#define PVERS L"1.71" // wide string #define PVERS L"1.72" // wide string
#define PVERSA "1.71" // ANSI string (windres 2.16.91 didn't like L) #define PVERSA "1.72" // ANSI string (windres 2.16.91 didn't like L)
#define PVERE L"171" // wide environment string #define PVERE L"172" // wide environment string
#define PVEREA "171" // ANSI environment string #define PVEREA "172" // ANSI environment string
#define PVERB 1,7,1,0 // binary (resource) #define PVERB 1,7,2,0 // binary (resource)
#ifdef _WIN64 #ifdef _WIN64
# define BITS L"64" # define BITS L"64"